display.cpp

Go to the documentation of this file.
00001 /* $Id: display.cpp 53928 2012-04-14 03:55:07Z bloodycoin $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Routines to set up the display, scroll and zoom the map.
00019  */
00020 
00021 #include "builder.hpp"
00022 #include "cursor.hpp"
00023 #include "display.hpp"
00024 #include "foreach.hpp"
00025 #include "game_preferences.hpp"
00026 #include "gettext.hpp"
00027 #include "halo.hpp"
00028 #include "hotkeys.hpp"
00029 #include "language.hpp"
00030 #include "log.hpp"
00031 #include "marked-up_text.hpp"
00032 #include "map.hpp"
00033 #include "map_label.hpp"
00034 #include "minimap.hpp"
00035 #include "text.hpp"
00036 #include "time_of_day.hpp"
00037 #include "tooltips.hpp"
00038 #include "arrow.hpp"
00039 #include "tod_manager.hpp"
00040 #include "resources.hpp"
00041 
00042 #include "SDL_image.h"
00043 
00044 #ifdef __SUNPRO_CC
00045 // GCC doesn't have hypot in cmath so include it for Sun Studio
00046 #include <math.h>
00047 #endif
00048 #include <cmath>
00049 
00050 // Includes for bug #17573
00051 #if defined(__GLIBC__)
00052 #include <gnu/libc-version.h>
00053 #include <cstdio>
00054 #endif
00055 
00056 static lg::log_domain log_display("display");
00057 #define ERR_DP LOG_STREAM(err, log_display)
00058 #define LOG_DP LOG_STREAM(info, log_display)
00059 #define DBG_DP LOG_STREAM(debug, log_display)
00060 
00061 namespace {
00062     const int DefaultZoom = 72;
00063     const int SmallZoom   = DefaultZoom / 2;
00064 
00065     const int MinZoom = 4;
00066     const int MaxZoom = 200;
00067     size_t sunset_delay = 0;
00068 
00069     bool benchmark = false;
00070 
00071     bool debug_foreground = false;
00072 }
00073 
00074 int display::last_zoom_ = SmallZoom;
00075 
00076 display::display(unit_map* units, CVideo& video, const gamemap* map, const std::vector<team>* t,const config& theme_cfg, const config& level) :
00077     units_(units),
00078     exclusive_unit_draw_requests_(),
00079     screen_(video),
00080     map_(map),
00081     currentTeam_(0),
00082     teams_(t),
00083     viewpoint_(NULL),
00084     energy_bar_rects_(),
00085     xpos_(0),
00086     ypos_(0),
00087     theme_(theme_cfg, screen_area()),
00088     zoom_(DefaultZoom),
00089     builder_(new terrain_builder(level, map, theme_.border().tile_image)),
00090     minimap_(NULL),
00091     minimap_location_(empty_rect),
00092     redrawMinimap_(false),
00093     redraw_background_(true),
00094     invalidateAll_(true),
00095     grid_(false),
00096     diagnostic_label_(0),
00097     panelsDrawn_(false),
00098     turbo_speed_(2),
00099     turbo_(false),
00100     invalidateGameStatus_(true),
00101     map_labels_(new map_labels(*this, 0)),
00102     scroll_event_("scrolled"),
00103     complete_redraw_event_("completely_redrawn"),
00104     nextDraw_(0),
00105     reportRects_(),
00106     reportSurfaces_(),
00107     reports_(),
00108     buttons_(),
00109     invalidated_(),
00110     previous_invalidated_(),
00111     mouseover_hex_overlay_(NULL),
00112     tod_hex_mask1(NULL),
00113     tod_hex_mask2(NULL),
00114     fog_images_(),
00115     shroud_images_(),
00116     selectedHex_(),
00117     mouseoverHex_(),
00118     keys_(),
00119     animate_map_(true),
00120     local_tod_light_(false),
00121     activeTeam_(0),
00122     drawing_buffer_(),
00123     map_screenshot_(false),
00124     fps_handle_(0),
00125     invalidated_hexes_(0),
00126     drawn_hexes_(0),
00127     idle_anim_(preferences::idle_anim()),
00128     idle_anim_rate_(1.0),
00129     map_screenshot_surf_(NULL),
00130     redraw_observers_(),
00131     draw_coordinates_(false),
00132     draw_terrain_codes_(false),
00133     arrows_map_(),
00134     color_adjust_()
00135 #if defined(__GLIBC__)
00136     , do_reverse_memcpy_workaround_(false)
00137 #endif
00138 {
00139     singleton_ = this;
00140 
00141     read(level.child_or_empty("display"));
00142 
00143     if(non_interactive()
00144         && (get_video_surface() != NULL
00145         && video.faked())) {
00146         screen_.lock_updates(true);
00147     }
00148 
00149     fill_images_list(game_config::fog_prefix, fog_images_);
00150     fill_images_list(game_config::shroud_prefix, shroud_images_);
00151 
00152     set_idle_anim_rate(preferences::idle_anim_rate());
00153 
00154     image::set_zoom(zoom_);
00155 
00156 #if defined(__GLIBC__)
00157     // Runtime checks for bug #17573
00158     // Get glibc runtime version information
00159     int glibc, glibc_minor;
00160     sscanf(gnu_get_libc_version(), "%d.%d", &glibc, &glibc_minor);
00161 
00162     // Get SDL runtime version information
00163     const SDL_version* v = SDL_Linked_Version();
00164 
00165     do_reverse_memcpy_workaround_ = (glibc > 2 || (glibc == 2 && glibc_minor >= 13)) &&
00166         (v->major < 1 || (v->major == 1 && v->minor < 2) ||
00167             (v->major == 1 && v->minor == 2 && v->patch < 15) );
00168 #endif
00169 }
00170 
00171 display::~display()
00172 {
00173     singleton_ = NULL;
00174 }
00175 
00176 struct is_energy_color {
00177     bool operator()(Uint32 color) const { return (color&0xFF000000) > 0x10000000 &&
00178                                                   (color&0x00FF0000) < 0x00100000 &&
00179                                                   (color&0x0000FF00) < 0x00001000 &&
00180                                                   (color&0x000000FF) < 0x00000010; }
00181 };
00182 
00183 
00184 const SDL_Rect& display::calculate_energy_bar(surface surf)
00185 {
00186     const std::map<surface,SDL_Rect>::const_iterator i = energy_bar_rects_.find(surf);
00187     if(i != energy_bar_rects_.end()) {
00188         return i->second;
00189     }
00190 
00191     int first_row = -1, last_row = -1, first_col = -1, last_col = -1;
00192 
00193     surface image(make_neutral_surface(surf));
00194 
00195     const_surface_lock image_lock(image);
00196     const Uint32* const begin = image_lock.pixels();
00197 
00198     for(int y = 0; y != image->h; ++y) {
00199         const Uint32* const i1 = begin + image->w*y;
00200         const Uint32* const i2 = i1 + image->w;
00201         const Uint32* const itor = std::find_if(i1,i2,is_energy_color());
00202         const int count = std::count_if(itor,i2,is_energy_color());
00203 
00204         if(itor != i2) {
00205             if(first_row == -1) {
00206                 first_row = y;
00207             }
00208 
00209             first_col = itor - i1;
00210             last_col = first_col + count;
00211             last_row = y;
00212         }
00213     }
00214 
00215     const SDL_Rect res = create_rect(first_col
00216             , first_row
00217             , last_col-first_col
00218             , last_row+1-first_row);
00219     energy_bar_rects_.insert(std::pair<surface,SDL_Rect>(surf,res));
00220     return calculate_energy_bar(surf);
00221 }
00222 
00223 
00224 
00225 void display::draw_bar(const std::string& image, int xpos, int ypos,
00226         const map_location& loc, size_t height, double filled,
00227         const SDL_Color& col, fixed_t alpha)
00228 {
00229 
00230     filled = std::min<double>(std::max<double>(filled,0.0),1.0);
00231     height = static_cast<size_t>(height*get_zoom_factor());
00232 
00233     surface surf(image::get_image(image,image::SCALED_TO_HEX));
00234 
00235     // We use UNSCALED because scaling (and bilinear interpolation)
00236     // is bad for calculate_energy_bar.
00237     // But we will do a geometric scaling later.
00238     surface bar_surf(image::get_image(image));
00239     if(surf == NULL || bar_surf == NULL) {
00240         return;
00241     }
00242 
00243     // calculate_energy_bar returns incorrect results if the surface colors
00244     // have changed (for example, due to bilinear interpolation)
00245     const SDL_Rect& unscaled_bar_loc = calculate_energy_bar(bar_surf);
00246 
00247     SDL_Rect bar_loc;
00248     if (surf->w == bar_surf->w && surf->h == bar_surf->h)
00249       bar_loc = unscaled_bar_loc;
00250     else {
00251       const fixed_t xratio = fxpdiv(surf->w,bar_surf->w);
00252       const fixed_t yratio = fxpdiv(surf->h,bar_surf->h);
00253       const SDL_Rect scaled_bar_loc = create_rect(
00254                 fxptoi(unscaled_bar_loc. x * xratio)
00255               , fxptoi(unscaled_bar_loc. y * yratio + 127)
00256               , fxptoi(unscaled_bar_loc. w * xratio + 255)
00257               , fxptoi(unscaled_bar_loc. h * yratio + 255));
00258       bar_loc = scaled_bar_loc;
00259     }
00260 
00261     if(height > bar_loc.h) {
00262         height = bar_loc.h;
00263     }
00264 
00265     //if(alpha != ftofxp(1.0)) {
00266     //  surf.assign(adjust_surface_alpha(surf,alpha));
00267     //  if(surf == NULL) {
00268     //      return;
00269     //  }
00270     //}
00271 
00272     const size_t skip_rows = bar_loc.h - height;
00273 
00274     SDL_Rect top = create_rect(0, 0, surf->w, bar_loc.y);
00275     SDL_Rect bot = create_rect(0, bar_loc.y + skip_rows, surf->w, 0);
00276     bot.h = surf->w - bot.y;
00277 
00278     drawing_buffer_add(LAYER_UNIT_BAR, loc, xpos, ypos, surf, top);
00279     drawing_buffer_add(LAYER_UNIT_BAR, loc, xpos, ypos + top.h, surf, bot);
00280 
00281     size_t unfilled = static_cast<size_t>(height * (1.0 - filled));
00282 
00283     if(unfilled < height && alpha >= ftofxp(0.3)) {
00284         const Uint8 r_alpha = std::min<unsigned>(unsigned(fxpmult(alpha,255)),255);
00285         surface filled_surf = create_compatible_surface(bar_surf, bar_loc.w, height - unfilled);
00286         SDL_Rect filled_area = create_rect(0, 0, bar_loc.w, height-unfilled);
00287         sdl_fill_rect(filled_surf,&filled_area,SDL_MapRGBA(bar_surf->format,col.r,col.g,col.b, r_alpha));
00288         drawing_buffer_add(LAYER_UNIT_BAR, loc, xpos + bar_loc.x, ypos + bar_loc.y + unfilled, filled_surf);
00289     }
00290 }
00291 
00292 
00293 bool display::add_exclusive_draw(const map_location& loc, unit& unit)
00294 {
00295     if (loc.valid() && exclusive_unit_draw_requests_.find(loc) == exclusive_unit_draw_requests_.end())
00296     {
00297         exclusive_unit_draw_requests_[loc] = unit.id();
00298         return true;
00299     }
00300     else
00301     {
00302         return false;
00303     }
00304 }
00305 
00306 std::string display::remove_exclusive_draw(const map_location& loc)
00307 {
00308     std::string id = "";
00309     if(loc.valid())
00310     {
00311         id = exclusive_unit_draw_requests_[loc];
00312         //id will be set to the default "" string by the [] operator if the map doesn't have anything for that loc.
00313         exclusive_unit_draw_requests_.erase(loc);
00314     }
00315     return id;
00316 }
00317 
00318 const time_of_day & display::get_time_of_day(const map_location& /*loc*/) const
00319 {
00320     static time_of_day tod;
00321     return tod;
00322 }
00323 
00324 void display::update_tod() {
00325     const time_of_day& tod = get_time_of_day();
00326     tod_color col = color_adjust_ + tod.color;
00327     image::set_color_adjustment(col.r, col.g, col.b);
00328 }
00329 
00330 void display::adjust_color_overlay(int r, int g, int b) {
00331     color_adjust_ = tod_color(r, g, b);
00332     update_tod();
00333 }
00334 
00335 
00336 void display::fill_images_list(const std::string& prefix, std::vector<std::string>& images)
00337 {
00338     // search prefix.png, prefix1.png, prefix2.png ...
00339     for(int i=0; ; ++i){
00340         std::ostringstream s;
00341         s << prefix;
00342         if(i != 0)
00343             s << i;
00344         s << ".png";
00345         if(image::exists(s.str()))
00346             images.push_back(s.str());
00347         else if(i>0)
00348             break;
00349     }
00350     if (images.empty())
00351         images.push_back("");
00352 }
00353 
00354 const std::string& display::get_variant(const std::vector<std::string>& variants, const map_location &loc) const
00355 {
00356     //TODO use better noise function
00357     return variants[abs(loc.x + loc.y) % variants.size()];
00358 }
00359 
00360 void display::rebuild_all()
00361 {
00362     builder_->rebuild_all();
00363 }
00364 
00365 void display::reload_map()
00366 {
00367     redraw_background_ = true;
00368     builder_->reload_map();
00369 }
00370 
00371 void display::change_map(const gamemap* m)
00372 {
00373     map_ = m;
00374     builder_->change_map(m);
00375 }
00376 
00377 void display::change_units(unit_map* umap)
00378 {
00379     units_ = umap;
00380 }
00381 
00382 void display::change_teams(const std::vector<team>* teams)
00383 {
00384     teams_ = teams;
00385 }
00386 
00387 
00388 const SDL_Rect& display::max_map_area() const
00389 {
00390     static SDL_Rect max_area = {0, 0, 0, 0};
00391 
00392     // hex_size() is always a multiple of 4
00393     // and hex_width() a multiple of 3,
00394     // so there shouldn't be off-by-one-errors
00395     // due to rounding.
00396     // To display a hex fully on screen,
00397     // a little bit extra space is needed.
00398     // Also added the border two times.
00399     max_area.w  = static_cast<int>((get_map().w() + 2 * theme_.border().size + 1.0/3.0) * hex_width());
00400     max_area.h = static_cast<int>((get_map().h() + 2 * theme_.border().size + 0.5) * hex_size());
00401 
00402     return max_area;
00403 }
00404 
00405 const SDL_Rect& display::map_area() const
00406 {
00407     static SDL_Rect max_area;
00408     max_area = max_map_area();
00409 
00410     // if it's for map_screenshot, maximize and don't recenter
00411     if (map_screenshot_) {
00412         return max_area;
00413     }
00414 
00415     static SDL_Rect res;
00416     res = map_outside_area();
00417 
00418     if(max_area.w < res.w) {
00419         // map is smaller, center
00420         res.x += (res.w - max_area.w)/2;
00421         res.w = max_area.w;
00422     }
00423 
00424     if(max_area.h < res.h) {
00425         // map is smaller, center
00426         res.y += (res.h - max_area.h)/2;
00427         res.h = max_area.h;
00428     }
00429 
00430     return res;
00431 }
00432 
00433 bool display::outside_area(const SDL_Rect& area, const int x, const int y) const
00434 {
00435     const int x_thresh = hex_size();
00436     const int y_thresh = hex_size();
00437     return (x < area.x || x > area.x + area.w - x_thresh ||
00438         y < area.y || y > area.y + area.h - y_thresh);
00439 }
00440 
00441 // This function uses the screen as reference
00442 const map_location display::hex_clicked_on(int xclick, int yclick) const
00443 {
00444     const SDL_Rect& rect = map_area();
00445     if(point_in_rect(xclick,yclick,rect) == false) {
00446         return map_location();
00447     }
00448 
00449     xclick -= rect.x;
00450     yclick -= rect.y;
00451 
00452     return pixel_position_to_hex(xpos_ + xclick, ypos_ + yclick);
00453 }
00454 
00455 
00456 // This function uses the rect of map_area as reference
00457 const map_location display::pixel_position_to_hex(int x, int y) const
00458 {
00459     // adjust for the border
00460     x -= static_cast<int>(theme_.border().size * hex_width());
00461     y -= static_cast<int>(theme_.border().size * hex_size());
00462     // The editor can modify the border and this will result in a negative y
00463     // value. Instead of adding extra cases we just shift the hex. Since the
00464     // editor doesn't use the direction this is no problem.
00465     const int offset = y < 0 ? 1 : 0;
00466     if(offset) {
00467         x += hex_width();
00468         y += hex_size();
00469     }
00470     const int s = hex_size();
00471     const int tesselation_x_size = hex_width() * 2;
00472     const int tesselation_y_size = s;
00473     const int x_base = x / tesselation_x_size * 2;
00474     const int x_mod  = x % tesselation_x_size;
00475     const int y_base = y / tesselation_y_size;
00476     const int y_mod  = y % tesselation_y_size;
00477 
00478     int x_modifier = 0;
00479     int y_modifier = 0;
00480 
00481     if (y_mod < tesselation_y_size / 2) {
00482         if ((x_mod * 2 + y_mod) < (s / 2)) {
00483             x_modifier = -1;
00484             y_modifier = -1;
00485         } else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
00486             x_modifier = 0;
00487             y_modifier = 0;
00488         } else {
00489             x_modifier = 1;
00490             y_modifier = -1;
00491         }
00492 
00493     } else {
00494         if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
00495             x_modifier = -1;
00496             y_modifier = 0;
00497         } else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
00498             x_modifier = 0;
00499             y_modifier = 0;
00500         } else {
00501             x_modifier = 1;
00502             y_modifier = 0;
00503         }
00504     }
00505 
00506     return map_location(x_base + x_modifier - offset, y_base + y_modifier - offset);
00507 }
00508 
00509 display::rect_of_hexes::iterator& display::rect_of_hexes::iterator::operator++()
00510 {
00511     if (loc_.y < rect_.bottom[loc_.x & 1])
00512         ++loc_.y;
00513     else {
00514         ++loc_.x;
00515         loc_.y = rect_.top[loc_.x & 1];
00516     }
00517 
00518     return *this;
00519 }
00520 
00521 // begin is top left, and end is after bottom right
00522 display::rect_of_hexes::iterator display::rect_of_hexes::begin() const
00523 {
00524     return iterator(map_location(left, top[left & 1]), *this);
00525 }
00526 display::rect_of_hexes::iterator display::rect_of_hexes::end() const
00527 {
00528     return iterator(map_location(right+1, top[(right+1) & 1]), *this);
00529 }
00530 
00531 const display::rect_of_hexes display::hexes_under_rect(const SDL_Rect& r) const
00532 {
00533     rect_of_hexes res;
00534 
00535     if (r.w<=0 || r.h<=0) {
00536         // empty rect, return dummy values giving begin=end
00537         res.left = 0;
00538         res.right = -1; // end is right+1
00539         res.top[0] = 0;
00540         res.top[1] = 0;
00541         res.bottom[0] = 0;
00542         res.bottom[1] = 0;
00543         return res;
00544     }
00545 
00546     SDL_Rect map_rect = map_area();
00547     // translate rect coordinates from screen-based to map_area-based
00548     int x = xpos_ - map_rect.x + r.x;
00549     int y = ypos_ - map_rect.y + r.y;
00550     // we use the "double" type to avoid important rounding error (size of an hex!)
00551     // we will also need to use std::floor to avoid bad rounding at border (negative values)
00552     double tile_width = hex_width();
00553     double tile_size = hex_size();
00554     double border = theme_.border().size;
00555     // we minus "0.(3)", for horizontal imbrication.
00556     // reason is: two adjacent hexes each overlap 1/4 of their width, so for
00557     // grid calculation 3/4 of tile width is used, which by default gives 
00558     // 18/54=0.(3). Note that, while tile_width is zoom dependand, 0.(3) is not.
00559     res.left = static_cast<int>(std::floor(-border + x / tile_width - 0.3333333));
00560     // we remove 1 pixel of the rectangle dimensions
00561     // (the rounded division take one pixel more than needed)
00562     res.right = static_cast<int>(std::floor(-border + (x + r.w-1) / tile_width));
00563 
00564     // for odd x, we must shift up one half-hex. Since x will vary along the edge,
00565     // we store here the y values for even and odd x, respectively
00566     res.top[0] = static_cast<int>(std::floor(-border + y / tile_size));
00567     res.top[1] = static_cast<int>(std::floor(-border + y / tile_size - 0.5));
00568     res.bottom[0] = static_cast<int>(std::floor(-border + (y + r.h-1) / tile_size));
00569     res.bottom[1] = static_cast<int>(std::floor(-border + (y + r.h-1) / tile_size - 0.5));
00570 
00571     // TODO: in some rare cases (1/16), a corner of the big rect is on a tile
00572     // (the 72x72 rectangle containing the hex) but not on the hex itself
00573     // Can maybe be optimized by using pixel_position_to_hex
00574 
00575     return res;
00576 }
00577 
00578 int display::get_location_x(const map_location& loc) const
00579 {
00580     return static_cast<int>(map_area().x + (loc.x + theme_.border().size) * hex_width() - xpos_);
00581 }
00582 
00583 int display::get_location_y(const map_location& loc) const
00584 {
00585     return static_cast<int>(map_area().y + (loc.y + theme_.border().size) * zoom_ - ypos_ + (is_odd(loc.x) ? zoom_/2 : 0));
00586 }
00587 
00588 map_location display::minimap_location_on(int x, int y)
00589 {
00590     //TODO: don't return location for this,
00591     // instead directly scroll to the clicked pixel position
00592 
00593     if (!point_in_rect(x, y, minimap_area())) {
00594         return map_location();
00595     }
00596 
00597     // we transform the coordinates from minimap to the full map image
00598     // probably more adjustments to do (border, minimap shift...)
00599     // but the mouse and human capacity to evaluate the rectangle center
00600     // is not pixel precise.
00601     int px = (x - minimap_location_.x) * get_map().w()*hex_width() / minimap_location_.w;
00602     int py = (y - minimap_location_.y) * get_map().h()*hex_size() / minimap_location_.h;
00603 
00604     map_location loc = pixel_position_to_hex(px, py);
00605     if (loc.x < 0)
00606         loc.x = 0;
00607     else if (loc.x >= get_map().w())
00608         loc.x = get_map().w() - 1;
00609 
00610     if (loc.y < 0)
00611         loc.y = 0;
00612     else if (loc.y >= get_map().h())
00613         loc.y = get_map().h() - 1;
00614 
00615     return loc;
00616 }
00617 
00618 int display::screenshot(std::string filename, bool map_screenshot)
00619 {
00620     int size = 0;
00621     if (!map_screenshot) {
00622         surface screenshot_surf = screen_.getSurface();
00623         SDL_SaveBMP(screenshot_surf, filename.c_str());
00624         size = screenshot_surf->w * screenshot_surf->h;
00625     } else {
00626         if (get_map().empty()) {
00627             // Map Screenshot are big, abort and warn the user if he does strange things
00628             std::cerr << "No map, can't do a Map Screenshot. If it was not wanted, check your hotkey.\n";
00629             return -1;
00630         }
00631 
00632         SDL_Rect area = max_map_area();
00633         map_screenshot_surf_ = create_compatible_surface(screen_.getSurface(), area.w, area.h);
00634 
00635         if (map_screenshot_surf_ == NULL) {
00636             // Memory problem ?
00637             std::cerr << "Can't create the screenshot surface. Maybe too big, try dezooming.\n";
00638             return -1;
00639         }
00640         size = map_screenshot_surf_->w * map_screenshot_surf_->h;
00641 
00642         // back up the current map view position and move to top-left
00643         int old_xpos = xpos_;
00644         int old_ypos = ypos_;
00645         xpos_ = 0;
00646         ypos_ = 0;
00647 
00648         // we reroute render output to the screenshot surface and invalidate all
00649         map_screenshot_= true ;
00650         invalidateAll_ = true;
00651         DBG_DP << "draw() with map_screenshot\n";
00652         draw(true,true);
00653 
00654         // finally save the image on disk
00655         SDL_SaveBMP(map_screenshot_surf_, filename.c_str());
00656 
00657         //NOTE: need to be sure that we free this huge surface (is it enough?)
00658         map_screenshot_surf_ = NULL;
00659 
00660         // restore normal rendering
00661         map_screenshot_= false;
00662         xpos_ = old_xpos;
00663         ypos_ = old_ypos;
00664         // some drawing functions are confused by the temporary change
00665         // of the map_area and thus affect the UI outside of the map
00666         redraw_everything();
00667     }
00668 
00669     // convert pixel size to BMP size
00670     size = (2048 + size*3);
00671     return size;
00672 }
00673 
00674 gui::button* display::find_button(const std::string& id)
00675 {
00676     for (size_t i = 0; i < buttons_.size(); ++i) {
00677         if(buttons_[i].id() == id) {
00678             return &buttons_[i];
00679         }
00680     }
00681     return NULL;
00682 }
00683 
00684 void display::create_buttons()
00685 {
00686     std::vector<gui::button> work;
00687 
00688     DBG_DP << "creating buttons...\n";
00689     const std::vector<theme::menu>& buttons = theme_.menus();
00690     for(std::vector<theme::menu>::const_iterator i = buttons.begin(); i != buttons.end(); ++i) {
00691         gui::button b(screen_,i->title(),string_to_button_type(i->type()),i->image());
00692         DBG_DP << "drawing button " << i->get_id() << "\n";
00693         b.set_id(i->get_id());
00694         const SDL_Rect& loc = i->location(screen_area());
00695         b.set_location(loc.x,loc.y);
00696         if (!i->tooltip().empty()){
00697             tooltips::add_tooltip(loc, i->tooltip());
00698         }
00699         if(rects_overlap(b.location(),map_outside_area())) {
00700             b.set_volatile(true);
00701         }
00702 
00703         gui::button* b_prev = find_button(b.id());
00704         if(b_prev) b.enable(b_prev->enabled());
00705 
00706         work.push_back(b);
00707     }
00708 
00709     buttons_.swap(work);
00710     DBG_DP << "buttons created\n";
00711 }
00712 
00713 gui::button::TYPE display::string_to_button_type(std::string type)
00714 {
00715     gui::button::TYPE res = gui::button::TYPE_PRESS;
00716     if (type == "checkbox") { res = gui::button::TYPE_CHECK; }
00717     else if (type == "image") { res = gui::button::TYPE_IMAGE; }
00718     return res;
00719 }
00720 
00721 static const std::string& get_direction(size_t n)
00722 {
00723     static std::string const dirs[6] = { "-n", "-ne", "-se", "-s", "-sw", "-nw" };
00724     return dirs[n >= sizeof(dirs)/sizeof(*dirs) ? 0 : n];
00725 }
00726 
00727 std::vector<surface> display::get_fog_shroud_images(const map_location& loc, image::TYPE image_type)
00728 {
00729     std::vector<std::string> names;
00730 
00731     map_location adjacent[6];
00732     get_adjacent_tiles(loc,adjacent);
00733 
00734     enum visibility {FOG=0, SHROUD=1, CLEAR=2};
00735     visibility tiles[6];
00736 
00737     const std::string* image_prefix[] =
00738         { &game_config::fog_prefix, &game_config::shroud_prefix};
00739 
00740     for(int i = 0; i != 6; ++i) {
00741         if(shrouded(adjacent[i])) {
00742             tiles[i] = SHROUD;
00743         } else if(!fogged(loc) && fogged(adjacent[i])) {
00744             tiles[i] = FOG;
00745         } else {
00746             tiles[i] = CLEAR;
00747         }
00748     }
00749 
00750     for(int v = FOG; v != CLEAR; ++v) {
00751         // Find somewhere that doesn't have overlap to use as a starting point
00752         int start;
00753         for(start = 0; start != 6; ++start) {
00754             if(tiles[start] != v) {
00755                 break;
00756             }
00757         }
00758 
00759         if(start == 6) {
00760             start = 0;
00761         }
00762 
00763         // Find all the directions overlap occurs from
00764         for(int i = (start+1)%6, n = 0; i != start && n != 6; ++n) {
00765             if(tiles[i] == v) {
00766                 std::ostringstream stream;
00767                 std::string name;
00768                 stream << *image_prefix[v];
00769 
00770                 for(int n = 0; v == tiles[i] && n != 6; i = (i+1)%6, ++n) {
00771                     stream << get_direction(i);
00772 
00773                     if(!image::exists(stream.str() + ".png")) {
00774                         // If we don't have any surface at all,
00775                         // then move onto the next overlapped area
00776                         if(name.empty()) {
00777                             i = (i+1)%6;
00778                         }
00779                         break;
00780                     } else {
00781                         name = stream.str();
00782                     }
00783                 }
00784 
00785                 if(!name.empty()) {
00786                     names.push_back(name + ".png");
00787                 }
00788             } else {
00789                 i = (i+1)%6;
00790             }
00791         }
00792     }
00793 
00794     // now get the surfaces
00795     std::vector<surface> res;
00796 
00797     foreach(std::string& name, names) {
00798         const surface surf(image::get_image(name, image_type));
00799         if (surf)
00800             res.push_back(surf);
00801     }
00802 
00803     return res;
00804 }
00805 
00806 std::vector<surface> display::get_terrain_images(const map_location &loc,
00807                              const std::string& timeid,
00808         image::TYPE image_type,
00809         TERRAIN_TYPE terrain_type)
00810 {
00811     std::vector<surface> res;
00812 
00813     terrain_builder::TERRAIN_TYPE builder_terrain_type =
00814           (terrain_type == FOREGROUND ?
00815           terrain_builder::FOREGROUND : terrain_builder::BACKGROUND);
00816 
00817     const terrain_builder::imagelist* const terrains = builder_->get_terrain_at(loc,
00818             timeid, builder_terrain_type);
00819 
00820     image::light_string lt;
00821     bool use_local_light = local_tod_light_;
00822     if(use_local_light){
00823         const time_of_day& tod = get_time_of_day(loc);
00824 
00825         //get all the light transitions
00826         map_location adjs[6];
00827         get_adjacent_tiles(loc,adjs);
00828         for(int d=0; d<6; ++d){
00829             const time_of_day& atod = get_time_of_day(adjs[d]);
00830             if(atod.color == tod.color)
00831                 continue;
00832 
00833             if(lt.empty()) {
00834                 //color the full hex before adding transitions
00835                 tod_color col = tod.color + color_adjust_;
00836                 lt = image::get_light_string(6, col.r, col.g, col.b);
00837             }
00838 
00839             // add the directional transitions
00840             tod_color acol = atod.color + color_adjust_;
00841             lt += image::get_light_string(d, acol.r, acol.g, acol.b);
00842         }
00843 
00844         if(lt.empty()){
00845             if(tod.color == get_time_of_day().color) {
00846                 use_local_light = false;
00847             } else {
00848                 tod_color col = tod.color + color_adjust_;
00849                 if(!col.is_zero()){
00850                     // no real lightmap needed but still color the hex
00851                     lt = image::get_light_string(-1, col.r, col.g, col.b);
00852                 }
00853             }
00854         }
00855     }
00856 
00857     if(terrains != NULL) {
00858         // Cache the offmap name.
00859         // Since it is themeabel it can change,
00860         // so don't make it static.
00861         const std::string off_map_name = "terrain/" + theme_.border().tile_image;
00862         for(std::vector<animated<image::locator> >::const_iterator it =
00863                 terrains->begin(); it != terrains->end(); ++it) {
00864 
00865             const image::locator &image = animate_map_ ?
00866                 it->get_current_frame() : it->get_first_frame();
00867 
00868             // We prevent ToD coloring and brightening of off-map tiles,
00869             // We need to test for the tile to be rendered and
00870             // not the location, since the transitions are rendered
00871             // over the offmap-terrain and these need a ToD coloring.
00872 
00873             surface surf;
00874             const bool off_map = image.get_filename() == off_map_name;
00875             if(!use_local_light || off_map) {
00876                 surf = image::get_image(image, off_map ? image::SCALED_TO_HEX : image_type);
00877             } else if(lt.empty()) {
00878                 surf = image::get_image(image, image::SCALED_TO_HEX);
00879             } else {
00880                 surf = image::get_lighted_image(image, lt, image::SCALED_TO_HEX);
00881             }
00882 
00883             if (!surf.null()) {
00884                 res.push_back(surf);
00885             }
00886         }
00887     }
00888 
00889     return res;
00890 }
00891 
00892 void display::drawing_buffer_add(const tdrawing_layer layer,
00893         const map_location& loc, int x, int y, const surface& surf,
00894         const SDL_Rect &clip)
00895 {
00896     drawing_buffer_.push_back(tblit(layer, loc, x, y, surf, clip));
00897 }
00898 
00899 void display::drawing_buffer_add(const tdrawing_layer layer,
00900         const map_location& loc, int x, int y,
00901         const std::vector<surface> &surf,
00902         const SDL_Rect &clip)
00903 {
00904     drawing_buffer_.push_back(tblit(layer, loc, x, y, surf, clip));
00905 }
00906 
00907 // FIXME: temporary method. Group splitting should be made
00908 // public into the definition of tdrawing_layer
00909 //
00910 // The drawing is done per layer_group, the range per group is [low, high).
00911 const display::tdrawing_layer display::drawing_buffer_key::layer_groups[] = {
00912     LAYER_TERRAIN_BG,
00913     LAYER_UNIT_FIRST,
00914     LAYER_UNIT_MOVE_DEFAULT,
00915     // Make sure the movement doesn't show above fog and reachmap.
00916     LAYER_REACHMAP,
00917     LAYER_LAST_LAYER
00918 };
00919 
00920 // no need to change this if layer_groups above is changed
00921 const unsigned int display::drawing_buffer_key::max_layer_group = sizeof(display::drawing_buffer_key::layer_groups) / sizeof(display::tdrawing_layer) - 2;
00922 
00923 enum {
00924     // you may adjust the following when needed:
00925 
00926     // maximum border. 3 should be safe even if a larger border is in use somewhere
00927     MAX_BORDER           = 3,
00928 
00929     // store x, y, and layer in one 32 bit integer
00930     // 4 most significant bits == layer group   => 16
00931     BITS_FOR_LAYER_GROUP = 4,
00932 
00933     // 10 second most significant bits == y     => 1024
00934     BITS_FOR_Y           = 10,
00935 
00936     // 1 third most significant bit == x parity => 2
00937     BITS_FOR_X_PARITY    = 1,
00938 
00939     // 8 fourth most significant bits == layer   => 256
00940     BITS_FOR_LAYER       = 8,
00941 
00942     // 9 least significant bits == x / 2        => 512 (really 1024 for x)
00943     BITS_FOR_X_OVER_2    = 9
00944 };
00945 
00946 inline display::drawing_buffer_key::drawing_buffer_key(const map_location &loc, tdrawing_layer layer)
00947     : key_(0)
00948 {
00949     // max_layer_group + 1 is the last valid entry in layer_groups, but it is always > layer
00950     // thus the first --g is a given => start with max_layer_groups right away
00951     unsigned int g = max_layer_group;
00952     while (layer < layer_groups[g]) {
00953         --g;
00954     }
00955 
00956     enum {
00957         SHIFT_LAYER          = BITS_FOR_X_OVER_2,
00958         SHIFT_X_PARITY       = BITS_FOR_LAYER + SHIFT_LAYER,
00959         SHIFT_Y              = BITS_FOR_X_PARITY + SHIFT_X_PARITY,
00960         SHIFT_LAYER_GROUP    = BITS_FOR_Y + SHIFT_Y
00961     };
00962     BOOST_STATIC_ASSERT(SHIFT_LAYER_GROUP + BITS_FOR_LAYER_GROUP == sizeof(key_) * 8);
00963 
00964     // the parity of x must be more significant than the layer but less significant than y.
00965     // Thus basically every row is split in two: First the row containing all the odd x
00966     // then the row containing all the even x. Since thus the least significant bit of x is
00967     // not required for x ordering anymore it can be shifted out to the right.
00968     const unsigned int x_parity = static_cast<unsigned int>(loc.x) & 1;
00969     key_  = (g << SHIFT_LAYER_GROUP) | (static_cast<unsigned int>(loc.y + MAX_BORDER) << SHIFT_Y);
00970     key_ |= (x_parity << SHIFT_X_PARITY);
00971     key_ |= (static_cast<unsigned int>(layer) << SHIFT_LAYER) | static_cast<unsigned int>(loc.x + MAX_BORDER) / 2;
00972 }
00973 
00974 void display::drawing_buffer_commit()
00975 {
00976     // std::list::sort() is a stable sort
00977     drawing_buffer_.sort();
00978 
00979     SDL_Rect clip_rect = map_area();
00980     surface screen = get_screen_surface();
00981     clip_rect_setter set_clip_rect(screen, &clip_rect);
00982 
00983     /*
00984      * Info regarding the rendering algorithm.
00985      *
00986      * In order to render a hex properly it needs to be rendered per row. On
00987      * this row several layers need to be drawn at the same time. Mainly the
00988      * unit and the background terrain. This is needed since both can spill
00989      * in the next hex. The foreground terrain needs to be drawn before to
00990      * avoid decapitation a unit.
00991      *
00992      * This ended in the following priority order:
00993      * layergroup > location > layer > 'tblit' > surface
00994      */
00995 
00996     foreach(const tblit &blit, drawing_buffer_) {
00997         foreach(const surface& surf, blit.surf()) {
00998             // Note that dstrect can be changed by sdl_blit
00999             // and so a new instance should be initialized
01000             // to pass to each call to sdl_blit.
01001             SDL_Rect dstrect = create_rect(blit.x(), blit.y(), 0, 0);
01002             SDL_Rect srcrect = blit.clip();
01003             SDL_Rect *srcrectArg = (srcrect.x | srcrect.y | srcrect.w | srcrect.h)
01004                 ? &srcrect : NULL;
01005             sdl_blit(surf, srcrectArg, screen, &dstrect);
01006             //NOTE: the screen part should already be marked as 'to update'
01007         }
01008     }
01009     drawing_buffer_clear();
01010 }
01011 
01012 void display::drawing_buffer_clear()
01013 {
01014     drawing_buffer_.clear();
01015 }
01016 
01017 void display::sunset(const size_t delay)
01018 {
01019     // This allow both parametric and toggle use
01020     sunset_delay = (sunset_delay == 0 && delay == 0) ? 3 : delay;
01021 }
01022 
01023 void display::toggle_benchmark()
01024 {
01025     benchmark = !benchmark;
01026 }
01027 
01028 void display::toggle_debug_foreground()
01029 {
01030     debug_foreground = !debug_foreground;
01031 }
01032 
01033 void display::flip()
01034 {
01035     if(video().faked()) {
01036         return;
01037     }
01038 
01039     surface frameBuffer = get_video_surface();
01040 
01041     // This is just the debug function "sunset" to progressively darken the map area
01042     static size_t sunset_timer = 0;
01043     if (sunset_delay && ++sunset_timer > sunset_delay) {
01044         sunset_timer = 0;
01045         SDL_Rect r = map_outside_area(); // Use frameBuffer to also test the UI
01046         const Uint32 color =  SDL_MapRGBA(video().getSurface()->format,0,0,0,255);
01047         // Adjust the alpha if you want to balance cpu-cost / smooth sunset
01048         fill_rect_alpha(r, color, 1, frameBuffer);
01049         update_rect(r);
01050     }
01051 
01052     font::draw_floating_labels(frameBuffer);
01053     events::raise_volatile_draw_event();
01054     cursor::draw(frameBuffer);
01055 
01056     video().flip();
01057 
01058     cursor::undraw(frameBuffer);
01059     events::raise_volatile_undraw_event();
01060     font::undraw_floating_labels(frameBuffer);
01061 }
01062 
01063 void display::update_display()
01064 {
01065     if (screen_.update_locked()) {
01066         return;
01067     }
01068 
01069     if(preferences::show_fps() || benchmark) {
01070         static int last_sample = SDL_GetTicks();
01071         static int frames = 0;
01072         ++frames;
01073         const int sample_freq = 10;
01074         if(frames == sample_freq) {
01075             const int this_sample = SDL_GetTicks();
01076 
01077             const int fps = (frames*1000)/(this_sample - last_sample);
01078             last_sample = this_sample;
01079             frames = 0;
01080 
01081             if(fps_handle_ != 0) {
01082                 font::remove_floating_label(fps_handle_);
01083                 fps_handle_ = 0;
01084             }
01085             std::ostringstream stream;
01086             stream << "fps: " << fps;
01087             if (game_config::debug) {
01088                 stream << "\nhex: " << drawn_hexes_*1.0/sample_freq;
01089                 if (drawn_hexes_ != invalidated_hexes_)
01090                     stream << " (" << (invalidated_hexes_-drawn_hexes_)*1.0/sample_freq << ")";
01091             }
01092             drawn_hexes_ = 0;
01093             invalidated_hexes_ = 0;
01094 
01095             font::floating_label flabel(stream.str());
01096             flabel.set_font_size(12);
01097             flabel.set_color(benchmark ? font::BAD_COLOR : font::NORMAL_COLOR);
01098             flabel.set_position(10, 100);
01099             flabel.set_alignment(font::LEFT_ALIGN);
01100 
01101             fps_handle_ = font::add_floating_label(flabel);
01102         }
01103     } else if(fps_handle_ != 0) {
01104         font::remove_floating_label(fps_handle_);
01105         fps_handle_ = 0;
01106         drawn_hexes_ = 0;
01107         invalidated_hexes_ = 0;
01108     }
01109 
01110     flip();
01111 }
01112 
01113 static void draw_panel(CVideo& video, const theme::panel& panel, std::vector<gui::button>& buttons)
01114 {
01115     //log_scope("draw panel");
01116     DBG_DP << "drawing panel " << panel.get_id() << "\n";
01117 
01118     surface surf(image::get_image(panel.image()));
01119 
01120     const SDL_Rect screen = screen_area();
01121     SDL_Rect& loc = panel.location(screen);
01122 
01123     DBG_DP << "panel location: x=" << loc.x << ", y=" << loc.y
01124             << ", w=" << loc.w << ", h=" << loc.h << "\n";
01125 
01126     if(!surf.null()) {
01127         if(surf->w != loc.w || surf->h != loc.h) {
01128             surf.assign(scale_surface(surf,loc.w,loc.h));
01129         }
01130 
01131         video.blit_surface(loc.x,loc.y,surf);
01132         update_rect(loc);
01133     }
01134 
01135     static bool first_time = true;
01136     for(std::vector<gui::button>::iterator b = buttons.begin(); b != buttons.end(); ++b) {
01137         if(rects_overlap(b->location(),loc)) {
01138             b->set_dirty(true);
01139             if (first_time){
01140                 /**
01141                  * @todo FixMe YogiHH:
01142                  * This is only made to have the buttons store their background
01143                  * information, otherwise the background will appear completely
01144                  * black. It would more straightforward to call bg_update, but
01145                  * that is not public and there seems to be no other way atm to
01146                  * call it. I will check if bg_update can be made public.
01147                  */
01148                 b->hide(true);
01149                 b->hide(false);
01150             }
01151         }
01152     }
01153 }
01154 
01155 static void draw_label(CVideo& video, surface target, const theme::label& label)
01156 {
01157     //log_scope("draw label");
01158 
01159         std::stringstream temp;
01160     Uint32 RGB=label.font_rgb();
01161         int red = (RGB & 0x00FF0000)>>16;
01162         int green = (RGB & 0x0000FF00)>>8;
01163         int blue = (RGB & 0x000000FF);
01164 
01165         std::string c_start="<";
01166         std::string c_sep=",";
01167         std::string c_end=">";
01168         std::stringstream color;
01169         color<< c_start << red << c_sep << green << c_sep << blue << c_end;
01170         std::string text = label.text();
01171 
01172         if(label.font_rgb_set()) {
01173         color<<text;
01174         text = color.str();
01175         }
01176     const std::string& icon = label.icon();
01177     SDL_Rect& loc = label.location(screen_area());
01178 
01179     if(icon.empty() == false) {
01180         surface surf(image::get_image(icon));
01181         if(!surf.null()) {
01182             if(surf->w > loc.w || surf->h > loc.h) {
01183                 surf.assign(scale_surface(surf,loc.w,loc.h));
01184             }
01185 
01186             sdl_blit(surf,NULL,target,&loc);
01187         }
01188 
01189         if(text.empty() == false) {
01190             tooltips::add_tooltip(loc,text);
01191         }
01192     } else if(text.empty() == false) {
01193         font::draw_text(&video,loc,label.font_size(),font::NORMAL_COLOR,text,loc.x,loc.y);
01194     }
01195 
01196     update_rect(loc);
01197 }
01198 
01199 void display::draw_all_panels()
01200 {
01201     surface const screen(screen_.getSurface());
01202 
01203     const std::vector<theme::panel>& panels = theme_.panels();
01204     for(std::vector<theme::panel>::const_iterator p = panels.begin(); p != panels.end(); ++p) {
01205         draw_panel(video(),*p,buttons_);
01206     }
01207 
01208     const std::vector<theme::label>& labels = theme_.labels();
01209     for(std::vector<theme::label>::const_iterator i = labels.begin(); i != labels.end(); ++i) {
01210         draw_label(video(),screen,*i);
01211     }
01212 
01213     create_buttons();
01214 }
01215 
01216 static void draw_background(surface screen, const SDL_Rect& area, const std::string& image)
01217 {
01218     const surface background(image::get_image(image));
01219     if(background.null()) {
01220         return;
01221     }
01222     const unsigned int width = background->w;
01223     const unsigned int height = background->h;
01224 
01225     const unsigned int w_count = static_cast<int>(std::ceil(static_cast<double>(area.w) / static_cast<double>(width)));
01226     const unsigned int h_count = static_cast<int>(std::ceil(static_cast<double>(area.h) / static_cast<double>(height)));
01227 
01228     for(unsigned int w = 0, w_off = area.x; w < w_count; ++w, w_off += width) {
01229         for(unsigned int h = 0, h_off = area.y; h < h_count; ++h, h_off += height) {
01230             SDL_Rect clip = create_rect(w_off, h_off, 0, 0);
01231             sdl_blit(background, NULL, screen, &clip);
01232         }
01233     }
01234 }
01235 
01236 void display::draw_text_in_hex(const map_location& loc,
01237         const tdrawing_layer layer, const std::string& text,
01238         size_t font_size, SDL_Color color, double x_in_hex, double y_in_hex)
01239 {
01240     if (text.empty()) return;
01241 
01242     const size_t font_sz = static_cast<size_t>(font_size * get_zoom_factor()
01243     );
01244 
01245     surface text_surf = font::get_rendered_text(text, font_sz, color);
01246     surface back_surf = font::get_rendered_text(text, font_sz, font::BLACK_COLOR);
01247     const int x = get_location_x(loc) - text_surf->w/2
01248                   + static_cast<int>(x_in_hex* hex_size());
01249     const int y = get_location_y(loc) - text_surf->h/2
01250                   + static_cast<int>(y_in_hex* hex_size());
01251 
01252     for (int dy=-1; dy <= 1; ++dy) {
01253         for (int dx=-1; dx <= 1; ++dx) {
01254             if (dx!=0 || dy!=0) {
01255                 drawing_buffer_add(layer, loc, x + dx, y + dy, back_surf);
01256             }
01257         }
01258     }
01259     drawing_buffer_add(layer, loc, x, y, text_surf);
01260 }
01261 
01262 void display::render_image(int x, int y, const display::tdrawing_layer drawing_layer,
01263         const map_location& loc, surface image,
01264         bool hreverse, bool greyscale, fixed_t alpha,
01265         Uint32 blendto, double blend_ratio, double submerged, bool vreverse)
01266 {
01267 
01268     if (image==NULL)
01269         return;
01270 
01271     SDL_Rect image_rect = create_rect(x, y, image->w, image->h);
01272     SDL_Rect clip_rect = map_area();
01273     if (!rects_overlap(image_rect, clip_rect))
01274         return;
01275 
01276     surface surf(image);
01277 
01278     if(hreverse) {
01279         surf = image::reverse_image(surf);
01280     }
01281     if(vreverse) {
01282         surf = flop_surface(surf, false);
01283     }
01284 
01285     if(greyscale) {
01286         surf = greyscale_image(surf, false);
01287     }
01288 
01289     if(blend_ratio != 0) {
01290         surf = blend_surface(surf, blend_ratio, blendto, false);
01291     }
01292     if(alpha > ftofxp(1.0)) {
01293         surf = brighten_image(surf, alpha, false);
01294     //} else if(alpha != 1.0 && blendto != 0) {
01295     //  surf.assign(blend_surface(surf,1.0-alpha,blendto));
01296     } else if(alpha != ftofxp(1.0)) {
01297         surf = adjust_surface_alpha(surf, alpha, false);
01298     }
01299 
01300     if(surf == NULL) {
01301         ERR_DP << "surface lost...\n";
01302         return;
01303     }
01304 
01305     if(submerged > 0.0) {
01306         // divide the surface into 2 parts
01307         const int submerge_height = std::max<int>(0, surf->h*(1.0-submerged));
01308         const int depth = surf->h - submerge_height;
01309         SDL_Rect srcrect = create_rect(0, 0, surf->w, submerge_height);
01310         drawing_buffer_add(drawing_layer, loc, x, y, surf, srcrect);
01311 
01312         if(submerge_height != surf->h) {
01313             //the lower part will be transparent
01314             float alpha_base = 0.3f; // 30% alpha at surface of water
01315             float alpha_delta = 0.015f; // lose 1.5% per pixel depth
01316             alpha_delta *= zoom_ / DefaultZoom; // adjust with zoom
01317             surf = submerge_alpha(surf, depth, alpha_base, alpha_delta, false);
01318 
01319             srcrect.y = submerge_height;
01320             srcrect.h = surf->h-submerge_height;
01321             y += submerge_height;
01322 
01323             drawing_buffer_add(drawing_layer, loc, x, y, surf, srcrect);
01324         }
01325     } else {
01326         // simple blit
01327         drawing_buffer_add(drawing_layer, loc, x, y, surf);
01328     }
01329 
01330 }
01331 
01332 void display::select_hex(map_location hex)
01333 {
01334     invalidate(selectedHex_);
01335     selectedHex_ = hex;
01336     invalidate(selectedHex_);
01337 }
01338 
01339 void display::highlight_hex(map_location hex)
01340 {
01341     invalidate(mouseoverHex_);
01342     mouseoverHex_ = hex;
01343     invalidate(mouseoverHex_);
01344 }
01345 
01346 void display::set_diagnostic(const std::string& msg)
01347 {
01348     if(diagnostic_label_ != 0) {
01349         font::remove_floating_label(diagnostic_label_);
01350         diagnostic_label_ = 0;
01351     }
01352 
01353     if(msg != "") {
01354         font::floating_label flabel(msg);
01355         flabel.set_font_size(font::SIZE_PLUS);
01356         flabel.set_color(font::YELLOW_COLOR);
01357         flabel.set_position(300, 50);
01358         flabel.set_clip_rect(map_outside_area());
01359 
01360         diagnostic_label_ = font::add_floating_label(flabel);
01361     }
01362 }
01363 
01364 void display::draw_init()
01365 {
01366     if (get_map().empty()) {
01367         return;
01368     }
01369 
01370     if(benchmark) {
01371         invalidateAll_ = true;
01372     }
01373 
01374     if(!panelsDrawn_) {
01375         draw_all_panels();
01376         panelsDrawn_ = true;
01377     }
01378 
01379     if(redraw_background_) {
01380         // Full redraw of the background
01381         const SDL_Rect clip_rect = map_outside_area();
01382         const surface screen = get_screen_surface();
01383         clip_rect_setter set_clip_rect(screen, &clip_rect);
01384         draw_background(screen, clip_rect, theme_.border().background_image);
01385         update_rect(clip_rect);
01386 
01387         redraw_background_ = false;
01388 
01389         // Force a full map redraw
01390         invalidateAll_ = true;
01391     }
01392 
01393     if(invalidateAll_) {
01394         DBG_DP << "draw() with invalidateAll\n";
01395 
01396         // toggle invalidateAll_ first to allow regular invalidations
01397         invalidateAll_ = false;
01398         invalidate_locations_in_rect(map_area());
01399 
01400         redrawMinimap_ = true;
01401     }
01402 }
01403 
01404 void display::draw_wrap(bool update, bool force)
01405 {
01406     static const int time_between_draws = preferences::draw_delay();
01407     const int current_time = SDL_GetTicks();
01408     const int wait_time = nextDraw_ - current_time;
01409 
01410     if(redrawMinimap_) {
01411         redrawMinimap_ = false;
01412         draw_minimap();
01413     }
01414 
01415     if(update) {
01416         update_display();
01417         if(!force && !benchmark && wait_time > 0) {
01418             // If it's not time yet to draw, delay until it is
01419             SDL_Delay(wait_time);
01420         }
01421 
01422         // Set the theoretical next draw time
01423         nextDraw_ += time_between_draws;
01424 
01425         // If the next draw already should have been finished,
01426         // we'll enter an update frenzy, so make sure that the
01427         // too late value doesn't keep growing.
01428         // Note: if force is used too often,
01429         // we can also get the opposite effect.
01430         nextDraw_ = std::max<int>(nextDraw_, SDL_GetTicks());
01431     }
01432 }
01433 
01434 void display::delay(unsigned int milliseconds) const
01435 {
01436     if (!game_config::no_delay)
01437         SDL_Delay(milliseconds);
01438 }
01439 
01440 const theme::menu* display::menu_pressed()
01441 {
01442     for(std::vector<gui::button>::iterator i = buttons_.begin(); i != buttons_.end(); ++i) {
01443         if(i->pressed()) {
01444             const size_t index = i - buttons_.begin();
01445             if(index >= theme_.menus().size()) {
01446                 assert(false);
01447                 return 0;
01448             }
01449             return &theme_.menus()[index];
01450         }
01451     }
01452 
01453     return 0;
01454 }
01455 
01456 void display::enable_menu(const std::string& item, bool enable)
01457 {
01458     for(std::vector<theme::menu>::const_iterator menu = theme_.menus().begin();
01459             menu != theme_.menus().end(); ++menu) {
01460 
01461         std::vector<std::string>::const_iterator hasitem =
01462             std::find(menu->items().begin(), menu->items().end(), item);
01463 
01464         if(hasitem != menu->items().end()) {
01465             const size_t index = menu - theme_.menus().begin();
01466             if(index >= buttons_.size()) {
01467                 assert(false);
01468                 return;
01469             }
01470             buttons_[index].enable(enable);
01471         }
01472     }
01473 }
01474 
01475 void display::announce(const std::string& message, const SDL_Color& color)
01476 {
01477     font::floating_label flabel(message);
01478     flabel.set_font_size(font::SIZE_XLARGE);
01479     flabel.set_color(color);
01480     flabel.set_position(map_outside_area().w/2, map_outside_area().h/3);
01481     flabel.set_lifetime(100);
01482     flabel.set_clip_rect(map_outside_area());
01483 
01484     font::add_floating_label(flabel);
01485 }
01486 
01487 void display::draw_border(const map_location& loc, const int xpos, const int ypos)
01488 {
01489     /**
01490      * at the moment the border must be between 0.0 and 0.5
01491      * and the image should always be prepared for a 0.5 border.
01492      * This way this code doesn't need modifications for other border sizes.
01493      */
01494 
01495     // First handle the corners :
01496     if(loc.x == -1 && loc.y == -1) { // top left corner
01497         drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
01498             image::get_image(theme_.border().corner_image_top_left, image::SCALED_TO_ZOOM));
01499     } else if(loc.x == get_map().w() && loc.y == -1) { // top right corner
01500         // We use the map idea of odd and even, and map coords are internal coords + 1
01501         if(loc.x%2 == 0) {
01502             drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos + zoom_/2,
01503                 image::get_image(theme_.border().corner_image_top_right_odd, image::SCALED_TO_ZOOM));
01504         } else {
01505             drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
01506                 image::get_image(theme_.border().corner_image_top_right_even, image::SCALED_TO_ZOOM));
01507         }
01508     } else if(loc.x == -1 && loc.y == get_map().h()) { // bottom left corner
01509         drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
01510             image::get_image(theme_.border().corner_image_bottom_left, image::SCALED_TO_ZOOM));
01511 
01512     } else if(loc.x == get_map().w() && loc.y == get_map().h()) { // bottom right corner
01513         // We use the map idea of odd and even, and map coords are internal coords + 1
01514         if(loc.x%2 == 1) {
01515             drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
01516                 image::get_image(theme_.border().corner_image_bottom_right_even, image::SCALED_TO_ZOOM));
01517         } else {
01518             drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
01519                 image::get_image(theme_.border().corner_image_bottom_right_odd, image::SCALED_TO_ZOOM));
01520         }
01521 
01522     // Now handle the sides:
01523     } else if(loc.x == -1) { // left side
01524         drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
01525             image::get_image(theme_.border().border_image_left, image::SCALED_TO_ZOOM));
01526     } else if(loc.x == get_map().w()) { // right side
01527         drawing_buffer_add(LAYER_BORDER, loc, xpos + zoom_/4, ypos,
01528             image::get_image(theme_.border().border_image_right, image::SCALED_TO_ZOOM));
01529     } else if(loc.y == -1) { // top side
01530         // We use the map idea of odd and even, and map coords are internal coords + 1
01531         if(loc.x%2 == 1) {
01532             drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
01533                 image::get_image(theme_.border().border_image_top_even, image::SCALED_TO_ZOOM));
01534         } else {
01535             drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos + zoom_/2,
01536                 image::get_image(theme_.border().border_image_top_odd, image::SCALED_TO_ZOOM));
01537         }
01538     } else if(loc.y == get_map().h()) { // bottom side
01539         // We use the map idea of odd and even, and map coords are internal coords + 1
01540         if(loc.x%2 == 1) {
01541             drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos,
01542                 image::get_image(theme_.border().border_image_bottom_even, image::SCALED_TO_ZOOM));
01543         } else {
01544             drawing_buffer_add(LAYER_BORDER, loc, xpos, ypos + zoom_/2,
01545                 image::get_image(theme_.border().border_image_bottom_odd, image::SCALED_TO_ZOOM));
01546         }
01547     }
01548 }
01549 
01550 void display::draw_minimap()
01551 {
01552     const SDL_Rect& area = minimap_area();
01553 
01554     if(area.w == 0 || area.h == 0) {
01555         return;
01556     }
01557 
01558     if(minimap_ == NULL || minimap_->w > area.w || minimap_->h > area.h) {
01559         minimap_ = image::getMinimap(area.w, area.h, get_map(), viewpoint_);
01560         if(minimap_ == NULL) {
01561             return;
01562         }
01563     }
01564 
01565     const surface screen(screen_.getSurface());
01566     clip_rect_setter clip_setter(screen, &area);
01567 
01568     SDL_Color back_color = {31,31,23,255};
01569     draw_centered_on_background(minimap_, area, back_color, screen);
01570 
01571     //update the minimap location for mouse and units functions
01572     minimap_location_.x = area.x + (area.w - minimap_->w) / 2;
01573     minimap_location_.y = area.y + (area.h - minimap_->h) / 2;
01574     minimap_location_.w = minimap_->w;
01575     minimap_location_.h = minimap_->h;
01576 
01577     draw_minimap_units();
01578 
01579     // calculate the visible portion of the map:
01580     // scaling between minimap and full map images
01581     double xscaling = 1.0*minimap_->w / (get_map().w()*hex_width());
01582     double yscaling = 1.0*minimap_->h / (get_map().h()*hex_size());
01583 
01584     // we need to shift with the border size
01585     // and the 0.25 from the minimap balanced drawing
01586     // and the possible difference between real map and outside off-map
01587     SDL_Rect map_rect = map_area();
01588     SDL_Rect map_out_rect = map_outside_area();
01589     double border = theme_.border().size;
01590     double shift_x = - border*hex_width() - (map_out_rect.w - map_rect.w) / 2;
01591     double shift_y = - (border+0.25)*hex_size() - (map_out_rect.h - map_rect.h) / 2;
01592 
01593     int view_x = static_cast<int>((xpos_ + shift_x) * xscaling);
01594     int view_y = static_cast<int>((ypos_ + shift_y) * yscaling);
01595     int view_w = static_cast<int>(map_out_rect.w * xscaling);
01596     int view_h = static_cast<int>(map_out_rect.h * yscaling);
01597 
01598     const Uint32 box_color = SDL_MapRGB(minimap_->format,0xFF,0xFF,0xFF);
01599     draw_rectangle(minimap_location_.x + view_x - 1,
01600                    minimap_location_.y + view_y - 1,
01601                    view_w + 2, view_h + 2,
01602                    box_color, screen);
01603 }
01604 
01605 void display::draw_minimap_units()
01606 {
01607     double xscaling = 1.0 * minimap_location_.w / get_map().w();
01608     double yscaling = 1.0 * minimap_location_.h / get_map().h();
01609 
01610     for(unit_map::const_iterator u = units_->begin(); u != units_->end(); ++u) {
01611         if (fogged(u->get_location()) ||
01612             ((*teams_)[currentTeam_].is_enemy(u->side()) &&
01613              u->invisible(u->get_location())) ||
01614              u->get_hidden()) {
01615             continue;
01616         }
01617 
01618         int side = u->side();
01619         const SDL_Color col = team::get_minimap_color(side);
01620         const Uint32 mapped_col = SDL_MapRGB(video().getSurface()->format,col.r,col.g,col.b);
01621 
01622         double u_x = u->get_location().x * xscaling;
01623         double u_y = (u->get_location().y + (is_odd(u->get_location().x) ? 1 : -1)/4.0) * yscaling;
01624         // use 4/3 to compensate the horizontal hexes imbrication
01625         double u_w = 4.0 / 3.0 * xscaling;
01626         double u_h = yscaling;
01627 
01628         SDL_Rect r = create_rect(minimap_location_.x + round_double(u_x)
01629                 , minimap_location_.y + round_double(u_y)
01630                 , round_double(u_w)
01631                 , round_double(u_h));
01632 
01633         sdl_fill_rect(video().getSurface(), &r, mapped_col);
01634     }
01635 }
01636 
01637 bool display::scroll(int xmove, int ymove)
01638 {
01639     const int orig_x = xpos_;
01640     const int orig_y = ypos_;
01641     xpos_ += xmove;
01642     ypos_ += ymove;
01643     bounds_check_position();
01644     const int dx = orig_x - xpos_; // dx = -xmove
01645     const int dy = orig_y - ypos_; // dy = -ymove
01646 
01647     // Only invalidate if we've actually moved
01648     if(dx == 0 && dy == 0)
01649         return false;
01650 
01651     font::scroll_floating_labels(dx, dy);
01652 
01653     surface screen(screen_.getSurface());
01654 
01655     SDL_Rect dstrect = map_area();
01656     dstrect.x += dx;
01657     dstrect.y += dy;
01658     dstrect = intersect_rects(dstrect, map_area());
01659 
01660     SDL_Rect srcrect = dstrect;
01661     srcrect.x -= dx;
01662     srcrect.y -= dy;
01663     if (!screen_.update_locked()) {
01664 
01665 // Hack to workaround bug #17573
01666 #if defined(__GLIBC__)
01667         if (do_reverse_memcpy_workaround_) {
01668             surface screen_copy = make_neutral_surface(screen);
01669             SDL_BlitSurface(screen_copy,&srcrect,screen,&dstrect);
01670         } else {
01671             SDL_BlitSurface(screen,&srcrect,screen,&dstrect);
01672         }
01673 #else
01674         SDL_BlitSurface(screen,&srcrect,screen,&dstrect);
01675 #endif
01676     }
01677 
01678 //This is necessary to avoid a crash in some SDL versions on some systems
01679 //see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=462794
01680 //FIXME remove this once the latest stable SDL release doesn't crash as 1.2.13 does
01681 #ifdef _MSC_VER
01682     __asm{cld};
01683 #elif defined(__GNUG__) && (defined(__i386__) || defined(__x86_64__))
01684     asm("cld");
01685 #endif
01686 
01687     // Invalidate locations in the newly visible rects
01688 
01689     if (dy != 0) {
01690         SDL_Rect r = map_area();
01691         if(dy < 0)
01692             r.y = r.y + r.h + dy;
01693         r.h = abs(dy);
01694         invalidate_locations_in_rect(r);
01695     }
01696     if (dx != 0) {
01697         SDL_Rect r = map_area();
01698         if (dx < 0)
01699             r.x = r.x + r.w + dx;
01700         r.w = abs(dx);
01701         invalidate_locations_in_rect(r);
01702     }
01703     scroll_event_.notify_observers();
01704     update_rect(map_area());
01705 
01706     redrawMinimap_ = true;
01707     return true;
01708 }
01709 
01710 void display::set_zoom(int amount)
01711 {
01712     int new_zoom = zoom_ + amount;
01713     if (new_zoom < MinZoom) {
01714         new_zoom = MinZoom;
01715     }
01716     if (new_zoom > MaxZoom) {
01717         new_zoom = MaxZoom;
01718     }
01719     if (new_zoom != zoom_) {
01720         SDL_Rect const &area = map_area();
01721         xpos_ += (xpos_ + area.w / 2) * amount / zoom_;
01722         ypos_ += (ypos_ + area.h / 2) * amount / zoom_;
01723 
01724         zoom_ = new_zoom;
01725         bounds_check_position();
01726         if (zoom_ != DefaultZoom) {
01727             last_zoom_ = zoom_;
01728         }
01729         image::set_zoom(zoom_);
01730 
01731         labels().recalculate_labels();
01732         redraw_background_ = true;
01733         invalidate_all();
01734 
01735         // Forces a redraw after zooming.
01736         // This prevents some graphic glitches from occurring.
01737         draw();
01738     }
01739 }
01740 
01741 void display::set_default_zoom()
01742 {
01743     if (zoom_ != DefaultZoom) {
01744         last_zoom_ = zoom_;
01745         set_zoom(DefaultZoom - zoom_ );
01746     } else {
01747         // When we are already at the default zoom,
01748         // switch to the last zoom used
01749         set_zoom(last_zoom_ - zoom_);
01750     }
01751 }
01752 
01753 bool display::tile_fully_on_screen(const map_location& loc)
01754 {
01755     int x = get_location_x(loc);
01756     int y = get_location_y(loc);
01757     return !outside_area(map_area(), x, y);
01758 }
01759 
01760 bool display::tile_nearly_on_screen(const map_location& loc) const
01761 {
01762     int x = get_location_x(loc);
01763     int y = get_location_y(loc);
01764     const SDL_Rect &area = map_area();
01765     int hw = hex_width(), hs = hex_size();
01766     return x + hs >= area.x - hw && x < area.x + area.w + hw &&
01767            y + hs >= area.y - hs && y < area.y + area.h + hs;
01768 }
01769 
01770 void display::scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type, bool force)
01771 {
01772     if(!force && !preferences::scroll_to_action()) return;
01773     if(screen_.update_locked()) {
01774         return;
01775     }
01776     const SDL_Rect area = map_area();
01777     const int xmove_expected = screenxpos - (area.x + area.w/2);
01778     const int ymove_expected = screenypos - (area.y + area.h/2);
01779 
01780     int xpos = xpos_ + xmove_expected;
01781     int ypos = ypos_ + ymove_expected;
01782     bounds_check_position(xpos, ypos);
01783     int xmove = xpos - xpos_;
01784     int ymove = ypos - ypos_;
01785 
01786     if(scroll_type == WARP || scroll_type == ONSCREEN_WARP || turbo_speed() > 2.0 || preferences::scroll_speed() > 99) {
01787         scroll(xmove,ymove);
01788         draw();
01789         return;
01790     }
01791 
01792     // Doing an animated scroll, with acceleration etc.
01793 
01794     int x_old = 0;
01795     int y_old = 0;
01796 
01797     const double dist_total = hypot(xmove, ymove);
01798     double dist_moved = 0.0;
01799 
01800     int t_prev = SDL_GetTicks();
01801 
01802     double velocity = 0.0;
01803     while (dist_moved < dist_total) {
01804         events::pump();
01805 
01806         int t = SDL_GetTicks();
01807         double dt = (t - t_prev) / 1000.0;
01808         if (dt > 0.200) {
01809             // Do not skip too many frames on slow PCs
01810             dt = 0.200;
01811         }
01812         t_prev = t;
01813 
01814         const double accel_time = 0.3 / turbo_speed(); // seconds until full speed is reached
01815         const double decel_time = 0.4 / turbo_speed(); // seconds from full speed to stop
01816 
01817         double velocity_max = preferences::scroll_speed() * 60.0;
01818         velocity_max *= turbo_speed();
01819         double accel = velocity_max / accel_time;
01820         double decel = velocity_max / decel_time;
01821 
01822         // If we started to decelerate now, where would we stop?
01823         double stop_time = velocity / decel;
01824         double dist_stop = dist_moved + velocity*stop_time - 0.5*decel*stop_time*stop_time;
01825         if (dist_stop > dist_total || velocity > velocity_max) {
01826             velocity -= decel * dt;
01827             if (velocity < 1.0) velocity = 1.0;
01828         } else {
01829             velocity += accel * dt;
01830             if (velocity > velocity_max) velocity = velocity_max;
01831         }
01832 
01833         dist_moved += velocity * dt;
01834         if (dist_moved > dist_total) dist_moved = dist_total;
01835 
01836         int x_new = round_double(xmove * dist_moved / dist_total);
01837         int y_new = round_double(ymove * dist_moved / dist_total);
01838 
01839         int dx = x_new - x_old;
01840         int dy = y_new - y_old;
01841 
01842         scroll(dx,dy);
01843         x_old += dx;
01844         y_old += dy;
01845         draw();
01846     }
01847 }
01848 
01849 void display::scroll_to_tile(const map_location& loc, SCROLL_TYPE scroll_type, bool check_fogged, bool force)
01850 {
01851     if(get_map().on_board(loc) == false) {
01852         ERR_DP << "Tile at " << loc << " isn't on the map, can't scroll to the tile.\n";
01853         return;
01854     }
01855 
01856     std::vector<map_location> locs;
01857     locs.push_back(loc);
01858     scroll_to_tiles(locs, scroll_type, check_fogged,false,0.0,force);
01859 }
01860 
01861 void display::scroll_to_tiles(map_location loc1, map_location loc2,
01862                               SCROLL_TYPE scroll_type, bool check_fogged,
01863                   double add_spacing, bool force)
01864 {
01865     std::vector<map_location> locs;
01866     locs.push_back(loc1);
01867     locs.push_back(loc2);
01868     scroll_to_tiles(locs, scroll_type, check_fogged, false, add_spacing,force);
01869 }
01870 
01871 void display::scroll_to_tiles(const std::vector<map_location>& locs,
01872                               SCROLL_TYPE scroll_type, bool check_fogged,
01873                   bool only_if_possible, double add_spacing, bool force)
01874 {
01875     // basically we calculate the min/max coordinates we want to have on-screen
01876     int minx = 0;
01877     int maxx = 0;
01878     int miny = 0;
01879     int maxy = 0;
01880     bool valid = false;
01881 
01882     for(std::vector<map_location>::const_iterator itor = locs.begin(); itor != locs.end() ; ++itor) {
01883         if(get_map().on_board(*itor) == false) continue;
01884         if(check_fogged && fogged(*itor)) continue;
01885 
01886         int x = get_location_x(*itor);
01887         int y = get_location_y(*itor);
01888 
01889         if (!valid) {
01890             minx = x;
01891             maxx = x;
01892             miny = y;
01893             maxy = y;
01894             valid = true;
01895         } else {
01896             int minx_new = std::min<int>(minx,x);
01897             int miny_new = std::min<int>(miny,y);
01898             int maxx_new = std::max<int>(maxx,x);
01899             int maxy_new = std::max<int>(maxy,y);
01900             SDL_Rect r = map_area();
01901             r.x = minx_new;
01902             r.y = miny_new;
01903             if(outside_area(r, maxx_new, maxy_new)) {
01904                 // we cannot fit all locations to the screen
01905                 if (only_if_possible) return;
01906                 break;
01907             }
01908             minx = minx_new;
01909             miny = miny_new;
01910             maxx = maxx_new;
01911             maxy = maxy_new;
01912         }
01913     }
01914     //if everything is fogged or the locs list is empty
01915     if(!valid) return;
01916 
01917     if (scroll_type == ONSCREEN || ONSCREEN_WARP) {
01918         SDL_Rect r = map_area();
01919         int spacing = round_double(add_spacing*hex_size());
01920         r.x += spacing;
01921         r.y += spacing;
01922         r.w -= 2*spacing;
01923         r.h -= 2*spacing;
01924         if (!outside_area(r, minx,miny) && !outside_area(r, maxx,maxy)) {
01925             return;
01926         }
01927     }
01928 
01929     // let's do "normal" rectangle math from now on
01930     SDL_Rect locs_bbox;
01931     locs_bbox.x = minx;
01932     locs_bbox.y = miny;
01933     locs_bbox.w = maxx - minx + hex_size();
01934     locs_bbox.h = maxy - miny + hex_size();
01935 
01936     // target the center
01937     int target_x = locs_bbox.x + locs_bbox.w/2;
01938     int target_y = locs_bbox.y + locs_bbox.h/2;
01939 
01940     if (scroll_type == ONSCREEN || ONSCREEN_WARP) {
01941         // when doing an ONSCREEN scroll we do not center the target unless needed
01942         SDL_Rect r = map_area();
01943         int map_center_x = r.x + r.w/2;
01944         int map_center_y = r.y + r.h/2;
01945 
01946         int h = r.h;
01947         int w = r.w;
01948 
01949         // we do not want to be only inside the screen rect, but center a bit more
01950         double inside_frac = 0.5; // 0.0 = always center the target, 1.0 = scroll the minimum distance
01951         w = static_cast<int>(w * inside_frac);
01952         h = static_cast<int>(h * inside_frac);
01953 
01954         // shrink the rectangle by the size of the locations rectangle we found
01955         // such that the new task to fit a point into a rectangle instead of rectangle into rectangle
01956         w -= locs_bbox.w;
01957         h -= locs_bbox.h;
01958 
01959         if (w < 1) w = 1;
01960         if (h < 1) h = 1;
01961 
01962         r.x = target_x - w/2;
01963         r.y = target_y - h/2;
01964         r.w = w;
01965         r.h = h;
01966 
01967         // now any point within r is a possible target to scroll to
01968         // we take the one with the minimum distance to map_center
01969         // which will always be at the border of r
01970 
01971         if (map_center_x < r.x) {
01972             target_x = r.x;
01973             target_y = map_center_y;
01974             if (target_y < r.y) target_y = r.y;
01975             if (target_y > r.y+r.h-1) target_y = r.y+r.h-1;
01976         } else if (map_center_x > r.x+r.w-1) {
01977             target_x = r.x+r.w-1;
01978             target_y = map_center_y;
01979             if (target_y < r.y) target_y = r.y;
01980             if (target_y >= r.y+r.h) target_y = r.y+r.h-1;
01981         } else if (map_center_y < r.y) {
01982             target_y = r.y;
01983             target_x = map_center_x;
01984             if (target_x < r.x) target_x = r.x;
01985             if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
01986         } else if (map_center_y > r.y+r.h-1) {
01987             target_y = r.y+r.h-1;
01988             target_x = map_center_x;
01989             if (target_x < r.x) target_x = r.x;
01990             if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
01991         } else {
01992             ERR_DP << "Bug in the scrolling code? Looks like we would not need to scroll after all...\n";
01993             // keep the target at the center
01994         }
01995     }
01996 
01997     scroll_to_xy(target_x, target_y,scroll_type,force);
01998 }
01999 
02000 
02001 void display::bounds_check_position()
02002 {
02003     const int orig_zoom = zoom_;
02004 
02005     if(zoom_ < MinZoom) {
02006         zoom_ = MinZoom;
02007     }
02008 
02009     if(zoom_ > MaxZoom) {
02010         zoom_ = MaxZoom;
02011     }
02012 
02013     bounds_check_position(xpos_, ypos_);
02014 
02015     if(zoom_ != orig_zoom) {
02016         image::set_zoom(zoom_);
02017     }
02018 }
02019 
02020 void display::bounds_check_position(int& xpos, int& ypos)
02021 {
02022     const int tile_width = hex_width();
02023 
02024     // Adjust for the border 2 times
02025     const int xend = static_cast<int>(tile_width * (get_map().w() + 2 * theme_.border().size) + tile_width/3);
02026     const int yend = static_cast<int>(zoom_ * (get_map().h() + 2 * theme_.border().size) + zoom_/2);
02027 
02028     if(xpos > xend - map_area().w) {
02029         xpos = xend - map_area().w;
02030     }
02031 
02032     if(ypos > yend - map_area().h) {
02033         ypos = yend - map_area().h;
02034     }
02035 
02036     if(xpos < 0) {
02037         xpos = 0;
02038     }
02039 
02040     if(ypos < 0) {
02041         ypos = 0;
02042     }
02043 }
02044 
02045 double display::turbo_speed() const
02046 {
02047     bool res = turbo_;
02048     if(keys_[SDLK_LSHIFT] || keys_[SDLK_RSHIFT]) {
02049         res = !res;
02050     }
02051 
02052     res |= screen_.faked();
02053     if (res)
02054         return turbo_speed_;
02055     else
02056         return 1.0;
02057 }
02058 
02059 void display::set_idle_anim_rate(int rate)
02060 {
02061     idle_anim_rate_ = std::pow(2.0, -rate/10.0);
02062 }
02063 
02064 void display::redraw_everything()
02065 {
02066     if(screen_.update_locked())
02067         return;
02068 
02069     invalidateGameStatus_ = true;
02070 
02071     reportRects_.clear();
02072     reportSurfaces_.clear();
02073     reports_.clear();
02074 
02075     bounds_check_position();
02076 
02077     tooltips::clear_tooltips();
02078 
02079     theme_.set_resolution(screen_area());
02080 
02081     if(buttons_.empty() == false) {
02082         create_buttons();
02083     }
02084 
02085     panelsDrawn_ = false;
02086 
02087     labels().recalculate_labels();
02088 
02089     redraw_background_ = true;
02090 
02091     int ticks1 = SDL_GetTicks();
02092     invalidate_all();
02093     int ticks2 = SDL_GetTicks();
02094     draw(true,true);
02095     int ticks3 = SDL_GetTicks();
02096     LOG_DP << "invalidate and draw: " << (ticks3 - ticks2) << " and " << (ticks2 - ticks1) << "\n";
02097 
02098     foreach (boost::function<void(display&)> f, redraw_observers_) {
02099         f(*this);
02100     }
02101 
02102     complete_redraw_event_.notify_observers();
02103 }
02104 
02105 void display::add_redraw_observer(boost::function<void(display&)> f)
02106 {
02107     redraw_observers_.push_back(f);
02108 }
02109 
02110 void display::clear_redraw_observers()
02111 {
02112     redraw_observers_.clear();
02113 }
02114 
02115 void display::draw(bool update,bool force) {
02116 //  log_scope("display::draw");
02117     if (screen_.update_locked()) {
02118         return;
02119     }
02120 
02121     local_tod_light_ = has_time_area() && preferences::get("local_tod_lighting", true);
02122 
02123     draw_init();
02124     pre_draw();
02125     // invalidate all that needs to be invalidated
02126     invalidate_animations();
02127     // at this stage we have everything that needs to be invalidated for this redraw
02128     // save it as the previous invalidated, and merge with the previous invalidated_
02129     // we merge with the previous redraw because if a hex had a unit last redraw but
02130     // not this one, nobody will tell us to redraw (cleanup)
02131     previous_invalidated_.swap(invalidated_);
02132     invalidated_.insert(previous_invalidated_.begin(),previous_invalidated_.end());
02133     // these new invalidations can not cause any propagation because
02134     // if a hex was invalidated last turn but not this turn, then
02135     // * case of no unit in neighbor hex=> no propagation
02136     // * case of unit in hex but was there last turn=>its hexes are invalidated too
02137     // * case of unit in hex not there last turn => it moved, so was invalidated previously
02138     if(!get_map().empty()) {
02139         //int simulate_delay = 0;
02140 
02141         /*
02142          * draw_invalidated() also invalidates the halos, so also needs to be
02143          * ran if invalidated_.empty() == true.
02144          */
02145         if(!invalidated_.empty() || preferences::show_haloes()) {
02146             draw_invalidated();
02147             invalidated_.clear();
02148         }
02149         drawing_buffer_commit();
02150         post_commit();
02151         draw_sidebar();
02152 
02153         // Simulate slow PC:
02154         //SDL_Delay(2*simulate_delay + rand() % 20);
02155     }
02156     draw_wrap(update, force);
02157     post_draw();
02158 }
02159 
02160 map_labels& display::labels()
02161 {
02162     return *map_labels_;
02163 }
02164 
02165 const map_labels& display::labels() const
02166 {
02167     return *map_labels_;
02168 }
02169 
02170 void display::clear_screen()
02171 {
02172     surface disp(screen_.getSurface());
02173     SDL_Rect area = screen_area();
02174     sdl_fill_rect(disp, &area, SDL_MapRGB(disp->format, 0, 0, 0));
02175 }
02176 
02177 const SDL_Rect& display::get_clip_rect()
02178 {
02179     return map_area();
02180 }
02181 
02182 void display::draw_invalidated() {
02183 //  log_scope("display::draw_invalidated");
02184     SDL_Rect clip_rect = get_clip_rect();
02185     surface screen = get_screen_surface();
02186     clip_rect_setter set_clip_rect(screen, &clip_rect);
02187     foreach (const map_location& loc, invalidated_) {
02188         int xpos = get_location_x(loc);
02189         int ypos = get_location_y(loc);
02190 
02191         update_rect(xpos, ypos, zoom_, zoom_);
02192 
02193         const bool on_map = get_map().on_board(loc);
02194         SDL_Rect hex_rect = create_rect(xpos, ypos, zoom_, zoom_);
02195         if(!rects_overlap(hex_rect,clip_rect)) {
02196             continue;
02197         }
02198         draw_hex(loc);
02199         drawn_hexes_+=1;
02200         // If the tile is at the border, we start to blend it
02201         if(!on_map) {
02202              draw_border(loc, xpos, ypos);
02203         }
02204     }
02205     invalidated_hexes_ += invalidated_.size();
02206 
02207     foreach (const map_location& loc, invalidated_) {
02208         unit_map::iterator u_it = units_->find(loc);
02209         exclusive_unit_draw_requests_t::iterator request = exclusive_unit_draw_requests_.find(loc);
02210         if (u_it != units_->end()
02211                 && (request == exclusive_unit_draw_requests_.end() || request->second == u_it->id()))
02212             u_it->redraw_unit();
02213     }
02214 
02215 }
02216 
02217 void display::draw_hex(const map_location& loc) {
02218     int xpos = get_location_x(loc);
02219     int ypos = get_location_y(loc);
02220     image::TYPE image_type = get_image_type(loc);
02221     const bool on_map = get_map().on_board(loc);
02222     const bool off_map_tile = (get_map().get_terrain(loc) == t_translation::OFF_MAP_USER);
02223     const time_of_day& tod = get_time_of_day(loc);
02224     if(!shrouded(loc)) {
02225         // unshrouded terrain (the normal case)
02226         drawing_buffer_add(LAYER_TERRAIN_BG, loc, xpos, ypos,
02227             get_terrain_images(loc,tod.id, image_type, BACKGROUND));
02228 
02229         drawing_buffer_add(LAYER_TERRAIN_FG, loc, xpos, ypos,
02230             get_terrain_images(loc,tod.id,image_type, FOREGROUND));
02231 
02232         // Draw the grid, if that's been enabled
02233         if(grid_ && on_map && !off_map_tile) {
02234             static const image::locator grid_top(game_config::images::grid_top);
02235             drawing_buffer_add(LAYER_GRID_TOP, loc, xpos, ypos,
02236                 image::get_image(grid_top, image::TOD_COLORED));
02237             static const image::locator grid_bottom(game_config::images::grid_bottom);
02238             drawing_buffer_add(LAYER_GRID_BOTTOM, loc, xpos, ypos,
02239                 image::get_image(grid_bottom, image::TOD_COLORED));
02240         }
02241     }
02242 
02243     // Draw the time-of-day mask on top of the terrain in the hex.
02244     // tod may differ from tod if hex is illuminated.
02245     const std::string& tod_hex_mask = tod.image_mask;
02246     if(tod_hex_mask1 != NULL || tod_hex_mask2 != NULL) {
02247         drawing_buffer_add(LAYER_TERRAIN_FG, loc, xpos, ypos, tod_hex_mask1);
02248         drawing_buffer_add(LAYER_TERRAIN_FG, loc, xpos, ypos, tod_hex_mask2);
02249     } else if(!tod_hex_mask.empty()) {
02250         drawing_buffer_add(LAYER_TERRAIN_FG, loc, xpos, ypos,
02251             image::get_image(tod_hex_mask,image::SCALED_TO_HEX));
02252     }
02253 
02254     // Paint mouseover overlays
02255     if(loc == mouseoverHex_ && (on_map || (in_editor() && get_map().on_board_with_border(loc))) && mouseover_hex_overlay_ != NULL) {
02256         drawing_buffer_add(LAYER_MOUSEOVER_OVERLAY, loc, xpos, ypos, mouseover_hex_overlay_);
02257     }
02258 
02259     // Paint arrows
02260     arrows_map_t::const_iterator arrows_in_hex = arrows_map_.find(loc);
02261     if(arrows_in_hex != arrows_map_.end()) {
02262         foreach(arrow* const a, arrows_in_hex->second) {
02263             a->draw_hex(loc);
02264         }
02265     }
02266 
02267     // Apply shroud, fog and linger overlay
02268 
02269     if(shrouded(loc)) {
02270         // We apply void also on off-map tiles
02271         // to shroud the half-hexes too
02272         const std::string& shroud_image = get_variant(shroud_images_, loc);
02273         drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
02274             image::get_image(shroud_image, image_type));
02275     } else if(fogged(loc)) {
02276         const std::string& fog_image = get_variant(fog_images_, loc);
02277         drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos,
02278             image::get_image(fog_image, image_type));
02279     }
02280 
02281     if(!shrouded(loc)) {
02282         drawing_buffer_add(LAYER_FOG_SHROUD, loc, xpos, ypos, get_fog_shroud_images(loc, image_type));
02283     }
02284 
02285     if (on_map) {
02286         if (draw_coordinates_) {
02287             int off_x = xpos + hex_size()/2;
02288             int off_y = ypos + hex_size()/2;
02289             surface text = font::get_rendered_text(lexical_cast<std::string>(loc), font::SIZE_SMALL, font::NORMAL_COLOR);
02290             surface bg = create_neutral_surface(text->w, text->h);
02291             SDL_Rect bg_rect = create_rect(0, 0, text->w, text->h);
02292             sdl_fill_rect(bg, &bg_rect, 0xaa000000);
02293             off_x -= text->w / 2;
02294             if (draw_terrain_codes_) {
02295                 off_y -= text->h;
02296             } else {
02297                 off_y -= text->h / 2;
02298             }
02299             drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
02300             drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
02301         }
02302         if (draw_terrain_codes_ && (game_config::debug || !shrouded(loc))) {
02303             int off_x = xpos + hex_size()/2;
02304             int off_y = ypos + hex_size()/2;
02305             surface text = font::get_rendered_text(lexical_cast<std::string>(get_map().get_terrain(loc)), font::SIZE_SMALL, font::NORMAL_COLOR);
02306             surface bg = create_neutral_surface(text->w, text->h);
02307             SDL_Rect bg_rect = create_rect(0, 0, text->w, text->h);
02308             sdl_fill_rect(bg, &bg_rect, 0xaa000000);
02309             off_x -= text->w / 2;
02310             if (!draw_coordinates_) {
02311                 off_y -= text->h / 2;
02312             }
02313             drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, bg);
02314             drawing_buffer_add(LAYER_FOG_SHROUD, loc, off_x, off_y, text);
02315         }
02316     }
02317 
02318     if(debug_foreground) {
02319         drawing_buffer_add(LAYER_UNIT_DEFAULT, loc, xpos, ypos,
02320             image::get_image("terrain/foreground.png", image_type));
02321     }
02322 
02323 }
02324 
02325 image::TYPE display::get_image_type(const map_location& /*loc*/) {
02326     return image::TOD_COLORED;
02327 }
02328 
02329 void display::draw_sidebar() {
02330 
02331 }
02332 
02333 void display::draw_image_for_report(surface& img, SDL_Rect& rect)
02334 {
02335     SDL_Rect visible_area = get_non_transparent_portion(img);
02336     SDL_Rect target = rect;
02337     if(visible_area.x != 0 || visible_area.y != 0 || visible_area.w != img->w || visible_area.h != img->h) {
02338         if(visible_area.w == 0 || visible_area.h == 0) {
02339             return;
02340         }
02341 
02342         if(visible_area.w > rect.w || visible_area.h > rect.h) {
02343             img.assign(get_surface_portion(img,visible_area,false));
02344             img.assign(scale_surface(img,rect.w,rect.h));
02345             visible_area.x = 0;
02346             visible_area.y = 0;
02347             visible_area.w = img->w;
02348             visible_area.h = img->h;
02349         } else {
02350             target.x = rect.x + (rect.w - visible_area.w)/2;
02351             target.y = rect.y + (rect.h - visible_area.h)/2;
02352             target.w = visible_area.w;
02353             target.h = visible_area.h;
02354         }
02355 
02356         sdl_blit(img,&visible_area,screen_.getSurface(),&target);
02357     } else {
02358         if(img->w != rect.w || img->h != rect.h) {
02359             img.assign(scale_surface(img,rect.w,rect.h));
02360         }
02361 
02362         sdl_blit(img,NULL,screen_.getSurface(),&target);
02363     }
02364 }
02365 
02366 void display::refresh_report(std::string const &report_name, const config &_report)
02367 {
02368     const theme::status_item *item = theme_.get_status_item(report_name);
02369     if (!item) {
02370         reportSurfaces_[report_name].assign(NULL);
02371         return;
02372     }
02373 
02374     SDL_Rect &rect = reportRects_[report_name];
02375     const SDL_Rect &new_rect = item->location(screen_area());
02376     surface &surf = reportSurfaces_[report_name];
02377     /// @todo check to see if reports_ is ever updated
02378     config &report = reports_[report_name];
02379 
02380     // Report and its location is unchanged since last time. Do nothing.
02381     if (surf && rect == new_rect && report == _report) {
02382         return;
02383     }
02384 
02385     report = _report;
02386 
02387     if (surf) {
02388         sdl_blit(surf, NULL, screen_.getSurface(), &rect);
02389         update_rect(rect);
02390     }
02391 
02392     // If the rectangle has just changed, assign the surface to it
02393     if (!surf || new_rect != rect)
02394     {
02395         surf.assign(NULL);
02396         rect = new_rect;
02397 
02398         // If the rectangle is present, and we are blitting text,
02399         // then we need to backup the surface.
02400         // (Images generally won't need backing up,
02401         // unless they are transparent, but that is done later).
02402         if (rect.w > 0 && rect.h > 0) {
02403             surf.assign(get_surface_portion(screen_.getSurface(), rect));
02404             if (reportSurfaces_[report_name] == NULL) {
02405                 ERR_DP << "Could not backup background for report!\n";
02406             }
02407         }
02408         update_rect(rect);
02409     }
02410 
02411     tooltips::clear_tooltips(rect);
02412 
02413     if (report.empty()) return;
02414 
02415     int x = rect.x, y = rect.y;
02416 
02417     // Add prefix, postfix elements.
02418     // Make sure that they get the same tooltip
02419     // as the guys around them.
02420     std::string str = item->prefix();
02421     if (!str.empty()) {
02422         config &e = report.add_child_at("element", config(), 0);
02423         e["text"] = str;
02424         e["tooltip"] = report.child("element")["tooltip"];
02425     }
02426     str = item->postfix();
02427     if (!str.empty()) {
02428         config &e = report.add_child("element");
02429         e["text"] = str;
02430         e["tooltip"] = report.child("element", -1)["tooltip"];
02431     }
02432 
02433     // Loop through and display each report element.
02434     int tallest = 0;
02435     int image_count = 0;
02436     bool used_ellipsis = false;
02437     std::ostringstream ellipsis_tooltip;
02438     SDL_Rect ellipsis_area = rect;
02439 
02440     for (config::const_child_itors elements = report.child_range("element");
02441          elements.first != elements.second; ++elements.first)
02442     {
02443         SDL_Rect area = create_rect(x, y, rect.w + rect.x - x, rect.h + rect.y - y);
02444         if (area.h <= 0) break;
02445 
02446         std::string t = (*elements.first)["text"];
02447         if (!t.empty())
02448         {
02449             if (used_ellipsis) goto skip_element;
02450 
02451             // Draw a text element.
02452             font::ttext text;
02453             if (item->font_rgb_set()) {
02454                 text.set_foreground_color(item->font_rgb());
02455             }
02456             bool eol = false;
02457             if (t[t.size() - 1] == '\n') {
02458                 eol = true;
02459                 t = t.substr(0, t.size() - 1);
02460             }
02461             text.set_font_size(item->font_size());
02462             text.set_text(t, true);
02463             text.set_maximum_width(area.w);
02464             text.set_maximum_height(area.h, false);
02465             surface s = text.render();
02466 
02467             // check if next element is text with almost no space to show it
02468             const int minimal_text = 12; // width in pixels
02469             config::const_child_iterator ee = elements.first;
02470             if (!eol && rect.w - (x - rect.x + s->w) < minimal_text &&
02471                 ++ee != elements.second && !(*ee)["text"].empty())
02472             {
02473                 // make this element longer to trigger rendering of ellipsis
02474                 // (to indicate that next elements have not enough space)
02475                 //NOTE this space should be longer than minimal_text pixels
02476                 t = t + "    ";
02477                 text.set_text(t, true);
02478                 s = text.render();
02479                 // use the area of this element for next tooltips
02480                 used_ellipsis = true;
02481                 ellipsis_area.x = x;
02482                 ellipsis_area.y = y;
02483                 ellipsis_area.w = s->w;
02484                 ellipsis_area.h = s->h;
02485             }
02486 
02487             screen_.blit_surface(x, y, s);
02488             area.w = s->w;
02489             area.h = s->h;
02490             if (area.h > tallest) {
02491                 tallest = area.h;
02492             }
02493             if (eol) {
02494                 x = rect.x;
02495                 y += tallest;
02496                 tallest = 0;
02497             } else {
02498                 x += area.w;
02499             }
02500         }
02501         else if (!(t = (*elements.first)["image"].str()).empty())
02502         {
02503             if (used_ellipsis) goto skip_element;
02504 
02505             // Draw an image element.
02506             surface img(image::get_image(t));
02507 
02508             if (!img) {
02509                 ERR_DP << "could not find image for report: '" << t << "'\n";
02510                 continue;
02511             }
02512 
02513             if (area.w < img->w && image_count) {
02514                 // We have more than one image, and this one doesn't fit.
02515                 img = surface(image::get_image(game_config::images::ellipsis));
02516                 used_ellipsis = true;
02517             }
02518 
02519             if (img->w < area.w) area.w = img->w;
02520             if (img->h < area.h) area.h = img->h;
02521             draw_image_for_report(img, area);
02522 
02523             ++image_count;
02524             if (area.h > tallest) {
02525                 tallest = area.h;
02526             }
02527 
02528             if (!used_ellipsis) {
02529                 x += area.w;
02530             } else {
02531                 ellipsis_area = area;
02532             }
02533         }
02534         else
02535         {
02536             // No text nor image, skip this element
02537             continue;
02538         }
02539 
02540         skip_element:
02541         t = (*elements.first)["tooltip"].t_str().base_str();
02542         if (!t.empty()) {
02543             if (!used_ellipsis) {
02544                 tooltips::add_tooltip(area, t, (*elements.first)["help"].t_str().base_str());
02545             } else {
02546                 // Collect all tooltips for the ellipsis.
02547                 // TODO: need a better separator
02548                 // TODO: assign an action
02549                 ellipsis_tooltip << t;
02550                 config::const_child_iterator ee = elements.first;
02551                 if (++ee != elements.second)
02552                     ellipsis_tooltip << "\n  _________\n\n";
02553             }
02554         }
02555     }
02556 
02557     if (used_ellipsis) {
02558         tooltips::add_tooltip(ellipsis_area, ellipsis_tooltip.str());
02559     }
02560 }
02561 
02562 void display::invalidate_all()
02563 {
02564     DBG_DP << "invalidate_all()\n";
02565     invalidateAll_ = true;
02566 #ifdef _OPENMP
02567 #pragma omp critical(invalidated_)
02568 #endif //_OPENMP
02569     invalidated_.clear();
02570     update_rect(map_area());
02571 }
02572 
02573 bool display::invalidate(const map_location& loc)
02574 {
02575     if(invalidateAll_)
02576         return false;
02577 
02578     bool tmp;
02579 #ifdef _OPENMP
02580 #pragma omp critical(invalidated_)
02581 #endif //_OPENMP
02582     tmp = invalidated_.insert(loc).second;
02583     return tmp;
02584 }
02585 
02586 bool display::invalidate(const std::set<map_location>& locs)
02587 {
02588     if(invalidateAll_)
02589         return false;
02590     bool ret = false;
02591     foreach (const map_location& loc, locs) {
02592 #ifdef _OPENMP
02593 #pragma omp critical(invalidated_)
02594 #endif //_OPENMP
02595         ret = invalidated_.insert(loc).second || ret;
02596     }
02597     return ret;
02598 }
02599 
02600 bool display::propagate_invalidation(const std::set<map_location>& locs)
02601 {
02602     if(invalidateAll_)
02603         return false;
02604 
02605     if(locs.size()<=1)
02606         return false; // propagation never needed
02607 
02608     bool result = false;
02609 #ifdef _OPENMP
02610 #pragma omp critical(invalidated_)
02611 #endif //_OPENMP
02612     {
02613         // search the first hex invalidated (if any)
02614         std::set<map_location>::const_iterator i = locs.begin();
02615         for(; i != locs.end() && invalidated_.count(*i) == 0 ; ++i) {}
02616 
02617         if (i != locs.end()) {
02618 
02619             // propagate invalidation
02620             // 'i' is already in, but I suspect that splitting the range is bad
02621             // especially because locs are often adjacents
02622             size_t previous_size = invalidated_.size();
02623             invalidated_.insert(locs.begin(), locs.end());
02624             result = previous_size < invalidated_.size();
02625         }
02626     }
02627     return result;
02628 }
02629 
02630 bool display::invalidate_visible_locations_in_rect(const SDL_Rect& rect)
02631 {
02632     return invalidate_locations_in_rect(intersect_rects(map_area(),rect));
02633 }
02634 
02635 bool display::invalidate_locations_in_rect(const SDL_Rect& rect)
02636 {
02637     if(invalidateAll_)
02638         return false;
02639 
02640     bool result = false;
02641     foreach (const map_location &loc, hexes_under_rect(rect)) {
02642         result |= invalidate(loc);
02643     }
02644     return result;
02645 }
02646 
02647 void display::invalidate_animations()
02648 {
02649     new_animation_frame();
02650     animate_map_ = preferences::animate_map();
02651     if (animate_map_) {
02652         foreach (const map_location &loc, get_visible_hexes())
02653         {
02654             if (shrouded(loc)) continue;
02655             if (builder_->update_animation(loc)) {
02656                 invalidate(loc);
02657             } else {
02658                 invalidate_animations_location(loc);
02659             }
02660         }
02661     }
02662     foreach (unit& u, *units_) {
02663         u.refresh();
02664     }
02665     std::vector<unit*> unit_list;
02666     foreach (unit &u, *units_) {
02667         unit_list.push_back(&u);
02668     }
02669     bool new_inval;
02670     do {
02671         new_inval = false;
02672 #ifdef _OPENMP
02673 #pragma omp parallel for reduction(|:new_inval) shared(unit_list) schedule(guided)
02674 #endif //_OPENMP
02675         for(int i=0; i < static_cast<int>(unit_list.size()); i++) {
02676             new_inval |=  unit_list[i]->invalidate(unit_list[i]->get_location());
02677         }
02678     }while(new_inval);
02679 }
02680 
02681 void display::add_arrow(arrow& arrow)
02682 {
02683     const arrow_path_t & arrow_path = arrow.get_path();
02684     foreach (const map_location& loc, arrow_path)
02685     {
02686         arrows_map_[loc].push_back(&arrow);
02687     }
02688 }
02689 
02690 void display::remove_arrow(arrow& arrow)
02691 {
02692     const arrow_path_t & arrow_path = arrow.get_path();
02693     foreach (const map_location& loc, arrow_path)
02694     {
02695         arrows_map_[loc].remove(&arrow);
02696     }
02697 }
02698 
02699 void display::update_arrow(arrow & arrow)
02700 {
02701     const arrow_path_t & previous_path = arrow.get_previous_path();
02702     foreach (const map_location& loc, previous_path)
02703     {
02704         arrows_map_[loc].remove(&arrow);
02705     }
02706     const arrow_path_t & arrow_path = arrow.get_path();
02707     foreach (const map_location& loc, arrow_path)
02708     {
02709         arrows_map_[loc].push_back(&arrow);
02710     }
02711 }
02712 
02713 void display::write(config& cfg) const
02714 {
02715     cfg["color_adjust_red"] = color_adjust_.r;
02716     cfg["color_adjust_green"] = color_adjust_.g;
02717     cfg["color_adjust_blue_"] = color_adjust_.b;
02718 }
02719 
02720 void display::read(const config& cfg)
02721 {
02722     color_adjust_.r = cfg["color_adjust_red"].to_int(0);
02723     color_adjust_.g = cfg["color_adjust_green"].to_int(0);
02724     color_adjust_.b = cfg["color_adjust_blue_"].to_int(0);
02725 }
02726 
02727 display *display::singleton_ = NULL;
02728 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Thu May 24 2012 01:02:33 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs