Fix FR / bug #9999: Preserve aspect ratio of the minimap.

- Try to correct a maximum of little 1-2 pixels imprecisions of the
previous system (but those are probably only visible with magnifing
tools).

- Clean/simplify the related functions

TODO: center minimaps in all remaining dialogs (currently only in create_game)
This commit is contained in:
Ali El Gariani 2007-09-27 19:56:29 +00:00
parent 16f52f0e3c
commit a83efa6dba
8 changed files with 110 additions and 106 deletions

View File

@ -69,7 +69,8 @@ display::display(CVideo& video, const gamemap& map, const config& theme_cfg, con
theme_(theme_cfg,screen_area()),
zoom_(DefaultZoom), last_zoom_(SmallZoom),
builder_(cfg, level, map, theme_.border().tile_image),
minimap_(NULL), redrawMinimap_(false), redraw_background_(true),
minimap_(NULL), minimap_location_(empty_rect),
redrawMinimap_(false), redraw_background_(true),
invalidateAll_(true), grid_(false),
diagnostic_label_(0), panelsDrawn_(false),
turbo_speed_(2), turbo_(false),
@ -322,17 +323,21 @@ int display::get_location_y(const gamemap::location& loc) const
gamemap::location display::minimap_location_on(int x, int y)
{
const SDL_Rect rect = minimap_area();
//TODO: don't return location for this,
// instead directly scroll to the clicked pixel position
if(x < rect.x || y < rect.y ||
x >= rect.x + rect.w || y >= rect.y + rect.h) {
if (!point_in_rect(x, y, minimap_location_)) {
return gamemap::location();
}
const double xdiv = double(rect.w) / double(map_.w());
const double ydiv = double(rect.h) / double(map_.h());
// we transfom the coordinates from minimap to the full map image
// probably more adjustements to do (border, minimap shift...)
// but the mouse and human capacity to evaluate the rectangle center
// is not pixel precise.
int px = (x - minimap_location_.x) * map_.w()*hex_width() / minimap_location_.w;
int py = (y - minimap_location_.y) * map_.h()*hex_size() / minimap_location_.h;
return gamemap::location(int((x - rect.x)/xdiv),int((y-rect.y)/ydiv));
return pixel_position_to_hex(px, py);
}
void display::get_visible_hex_bounds(gamemap::location &topleft, gamemap::location &bottomright) const
@ -962,8 +967,7 @@ void display::draw_wrap(bool update,bool force,bool changed)
if(redrawMinimap_) {
redrawMinimap_ = false;
const SDL_Rect area = minimap_area();
draw_minimap(area.x,area.y,area.w,area.h);
draw_minimap();
changed = true;
}
@ -1067,19 +1071,6 @@ void display::announce(const std::string message, const SDL_Color& colour)
font::CENTER_ALIGN);
}
surface display::get_minimap(int w, int h)
{
if(minimap_ != NULL && (minimap_->w != w || minimap_->h != h)) {
minimap_ = NULL;
}
if(minimap_ == NULL) {
minimap_ = image::getMinimap(w, h, map_, viewpoint_);
}
return minimap_;
}
void display::draw_border(const gamemap::location& loc, const int xpos, const int ypos)
{
/**
@ -1172,42 +1163,46 @@ void display::draw_border(const gamemap::location& loc, const int xpos, const in
}
}
void display::draw_minimap(int x, int y, int w, int h)
void display::draw_minimap()
{
const surface surf(get_minimap(w,h));
if(surf == NULL) {
return;
const SDL_Rect& area = minimap_area();
if(minimap_ == NULL || minimap_->w > area.w || minimap_->h > area.h) {
minimap_ = image::getMinimap(area.w, area.h, map_, viewpoint_);
}
SDL_Rect minimap_location = {x,y,w,h};
clip_rect_setter clip_setter(video().getSurface(),minimap_location);
SDL_Rect loc = minimap_location;
SDL_BlitSurface(surf,NULL,video().getSurface(),&loc);
int map_w = map_.w(), map_h = map_.h();
draw_minimap_units(x, y, w, h);
const double xscaling = double(surf->w) / map_w;
const double yscaling = double(surf->h) / map_h;
const int tile_width = hex_width();
const int xbox = static_cast<int>(xscaling*(xpos_-tile_width)/tile_width);
const int ybox = static_cast<int>(yscaling*(ypos_-zoom_)/zoom_);
// The magic numbers experimentally determined, like in display::map_area()
const int wbox = static_cast<int>(xscaling*(map_area().w+(4.0/3.0)*tile_width)/tile_width - xscaling);
const int hbox = static_cast<int>(yscaling*(map_area().h+1.5*zoom_)/zoom_ - yscaling);
const Uint32 boxcolour = SDL_MapRGB(surf->format,0xFF,0xFF,0xFF);
const surface screen(screen_.getSurface());
clip_rect_setter clip_setter(screen, area);
draw_rectangle(x+xbox,y+ybox,wbox,hbox,boxcolour,screen);
SDL_Color back_color = {0,0,0,255};
draw_centered_on_background(minimap_, area, back_color, screen);
update_rect(minimap_location);
//update the minimap location for mouse and units functions
minimap_location_.x = area.x + (area.w - minimap_->w) / 2;
minimap_location_.y = area.y + (area.h - minimap_->h) / 2;
minimap_location_.w = minimap_->w;
minimap_location_.h = minimap_->h;
draw_minimap_units();
// calculate the visible portion of the map:
// scaling between minimap and full map images
double xscaling = 1.0*minimap_->w / (map_.w()*hex_width());
double yscaling = 1.0*minimap_->h / (map_.h()*hex_size());
// we need to shift with the border size
// and the 0.25 from the minimap balanced drawing
double border = theme_.border().size;
int view_x = static_cast<int>((xpos_ - border*hex_width()) * xscaling);
int view_y = static_cast<int>((ypos_ - (border+0.25)*hex_size()) * yscaling);
int view_w = static_cast<int>(map_outside_area().w * xscaling);
int view_h = static_cast<int>(map_outside_area().h * yscaling);
const Uint32 box_color = SDL_MapRGB(minimap_->format,0xFF,0xFF,0xFF);
draw_rectangle(minimap_location_.x + view_x - 1,
minimap_location_.y + view_y - 1,
view_w + 2, view_h + 2,
box_color, screen);
}
void display::scroll(int xmove, int ymove)
@ -1574,16 +1569,6 @@ void display::draw_image_for_report(surface& img, SDL_Rect& rect)
}
}
void display::recalculate_minimap()
{
if(minimap_ != NULL) {
minimap_.assign(NULL);
}
redraw_minimap();
// Remove unit after invalidating...
}
void display:: set_report_content(const reports::TYPE which_report, const std::string &content) {
report_[which_report] = content;
}

View File

@ -179,9 +179,7 @@ public:
// Will be overridden in the display subclass
virtual void invalidate(const gamemap::location& loc) {invalidated_.insert(loc);};
virtual void draw_minimap_units(int /* x */, int /* y */, int /* w */, int /* h */) {};
// this surface must be freed by the caller
surface get_minimap(int w, int h);
virtual void draw_minimap_units() {};
const gamemap& get_map()const { return map_;}
@ -325,7 +323,7 @@ public:
//! Schedule the minimap for recalculation.
//! Useful if any terrain in the map has changed.
void recalculate_minimap();
void recalculate_minimap() {minimap_ = NULL; redrawMinimap_ = true; };
//! Schedule the minimap to be redrawn.
//! Useful if units have moved about on the map.
@ -354,7 +352,7 @@ protected:
virtual void draw_border(const gamemap::location& loc,
const int xpos, const int ypos);
void draw_minimap(int x, int y, int w, int h);
void draw_minimap();
virtual void zoom_redraw_hook() {};
@ -378,6 +376,7 @@ protected:
int last_zoom_;
terrain_builder builder_;
surface minimap_;
SDL_Rect minimap_location_;
bool redrawMinimap_;
bool redraw_background_;
bool invalidateAll_;

View File

@ -529,8 +529,11 @@ void game_display::draw_sidebar()
}
}
void game_display::draw_minimap_units(int x, int y, int w, int h)
void game_display::draw_minimap_units()
{
double xscaling = 1.0 * minimap_location_.w / map_.w();
double yscaling = 1.0 * minimap_location_.h / map_.h();
for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u) {
if(fogged(u->first) ||
(teams_[currentTeam_].is_enemy(u->second.side()) &&
@ -541,10 +544,18 @@ void game_display::draw_minimap_units(int x, int y, int w, int h)
const int side = u->second.side();
const SDL_Color col = team::get_minimap_colour(side);
const Uint32 mapped_col = SDL_MapRGB(video().getSurface()->format,col.r,col.g,col.b);
SDL_Rect rect = { x + (u->first.x * w) / map_.w(),
y + (u->first.y * h + (is_odd(u->first.x) ? h / 2 : 0)) / map_.h(),
w / map_.w(), h / map_.h() };
SDL_FillRect(video().getSurface(),&rect,mapped_col);
double u_x = u->first.x * xscaling;
double u_y = (u->first.y + (is_odd(u->first.x) ? 1 : -1)/4.0) * yscaling;
// use 4/3 to compensate the horizontal hexes imbrication
double u_w = 4.0 / 3.0 * xscaling;
double u_h = yscaling;
SDL_Rect r = { minimap_location_.x + round_double(u_x),
minimap_location_.y + round_double(u_y),
round_double(u_w), round_double(u_h) };
SDL_FillRect(video().getSurface(), &r, mapped_col);
}
}

View File

@ -118,7 +118,7 @@ private:
//! Function to invalidate animated terrains which may have changed.
void invalidate_animations();
virtual void draw_minimap_units(int x, int y, int w, int h);
virtual void draw_minimap_units();
public:
//! Temporarily place a unit on map (moving: can overlap others).

View File

@ -98,34 +98,26 @@ surface getMinimap(int w, int h, const gamemap& map, const viewpoint* vw)
wassert(surf != NULL);
SDL_Rect maprect = {x*scale*3/4,y*scale + (is_odd(x) ? scale/2 : 0),0,0};
// we need a balanced shift up and down of the hexes.
// if not, only the bottom half-hexes are clipped
// and it looks asymetrical.
// also do 1-pixel shift because the scaling
// function seems to do it with its rounding
SDL_Rect maprect = {x * scale*3/4 - 1,
y*scale + scale/4 * (is_odd(x) ? 1 : -1) - 1,
0, 0};
SDL_BlitSurface(surf, NULL, minimap, &maprect);
}
}
}
if((minimap->w != w || minimap->h != h) && w != 0 && h != 0) {
const surface surf(minimap);
#if 0
// preserve the aspect ratio of the original map rather than
// distorting it to fit the minimap window.
//
// This needs more work. There are at least two issues:
// (1) the part of the minimap window outside the scaled map
// needs to be blacked out/invalidated.
// (2) the rather nasty code in draw_minimap_units needs to
// change.
float sw = 1.0, sh = 1.0;
if (minimap->h < minimap->w) sh = (minimap->h*1.0)/minimap->w;
if (minimap->w < minimap->h) sw = (minimap->w*1.0)/minimap->h;
w = int(w * sw);
h = int(h * sh);
#endif
minimap = surface(scale_surface(surf,w,h));
}
double wratio = w*1.0 / minimap->w;
double hratio = h*1.0 / minimap->h;
double ratio = minimum<double>(wratio, hratio);
minimap = scale_surface(minimap,
static_cast<int>(minimap->w * ratio), static_cast<int>(minimap->h * ratio));
LOG_DP << "done generating minimap\n";

View File

@ -486,11 +486,8 @@ void create::process_event()
#ifndef USE_TINY_GUI
if(map.get() != NULL) {
const surface mini(image::getMinimap(minimap_rect_.w,minimap_rect_.h,*map,0));
if(mini != NULL) {
SDL_Rect rect = minimap_rect_;
SDL_BlitSurface(mini, NULL, video().getSurface(), &rect);
update_rect(rect);
}
SDL_Color back_color = {0,0,0,255};
draw_centered_on_background(mini, minimap_rect_, back_color, video().getSurface());
}
#endif
@ -619,11 +616,8 @@ void create::hide_children(bool hide)
#ifndef USE_TINY_GUI
const surface mini(image::getMinimap(minimap_rect_.w,minimap_rect_.h,map,0));
if(mini != NULL) {
SDL_Rect rect = minimap_rect_;
SDL_BlitSurface(mini, NULL, video().getSurface(), &rect);
update_rect(rect);
}
SDL_Color back_color = {0,0,0,255};
draw_centered_on_background(mini, minimap_rect_, back_color, video().getSurface());
#endif
} catch(gamemap::incorrect_format_exception& e) {
LOG_STREAM(err,general) << "map could not be loaded: " << e.msg_ << "\n";

View File

@ -1343,3 +1343,20 @@ void draw_solid_tinted_rectangle(int x, int y, int w, int h,
SDL_Rect rect = {x,y,w,h};
fill_rect_alpha(rect,SDL_MapRGB(target->format,r,g,b),Uint8(alpha*255),target);
}
void draw_centered_on_background(surface surf, const SDL_Rect& rect, const SDL_Color& color, surface target)
{
clip_rect_setter clip_setter(target, rect);
Uint32 col = SDL_MapRGBA(target->format, color.r, color.g, color.b, color.unused);
//TODO: only draw background outside the image
SDL_Rect r = rect;
SDL_FillRect(target, &r, col);
if (surf != NULL) {
r.x = rect.x + (rect.w-surf->w)/2;
r.y = rect.y + (rect.h-surf->h)/2;
SDL_BlitSurface(surf, NULL, target, &r);
}
update_rect(rect);
}

View File

@ -248,4 +248,10 @@ void draw_rectangle(int x, int y, int w, int h, Uint32 colour, surface tg);
void draw_solid_tinted_rectangle(int x, int y, int w, int h,
int r, int g, int b,
double alpha, surface target);
// blit the image on the center of the rectangle
// and a add a colored background
void draw_centered_on_background(surface surf, const SDL_Rect& rect,
const SDL_Color& color, surface target);
#endif