widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #01394
[Merge] lp:~widelands-dev/widelands/minimap into lp:widelands
cghislai has proposed merging lp:~widelands-dev/widelands/minimap into lp:widelands.
Requested reviews:
Widelands Developers (widelands-dev)
Related bugs:
Bug #846409 in widelands: "Improving the load game dialog"
https://bugs.launchpad.net/widelands/+bug/846409
Bug #1202146 in widelands: "With opengl enabled, screenshots display edges in terrain strangely"
https://bugs.launchpad.net/widelands/+bug/1202146
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/minimap/+merge/176945
This needs careful review. I redesigned the maprenderer so it returns a surface pointer. The minimap panel does the blit by itself now. The new method is also used in the game preload to save the minimap as a png image. This image is displayed in the load game screen.
I used in_mermoy_image for dealing with minimap images. There is a big warning in this class but the image is mostly destroyed right after its creation, or in the case of the load screen the pointer is kept safe. I also modified the destructor to make sure the surface is released upon destruction.
I also added clearing of the alpha channel when locking the screen surface to work around the transparency issue with screenshots.
--
https://code.launchpad.net/~widelands-dev/widelands/minimap/+merge/176945
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/minimap into lp:widelands.
=== modified file 'src/game_io/game_preload_data_packet.cc'
--- src/game_io/game_preload_data_packet.cc 2013-07-21 10:00:09 +0000
+++ src/game_io/game_preload_data_packet.cc 2013-07-25 14:01:30 +0000
@@ -19,12 +19,21 @@
#include "game_preload_data_packet.h"
+#include "graphic/graphic.h"
+#include "graphic/surface.h"
+#include "graphic/in_memory_image.h"
+#include "graphic/render/minimaprenderer.h"
#include "logic/game.h"
#include "logic/game_data_error.h"
#include "logic/map.h"
#include "profile/profile.h"
#include "scripting/scripting.h"
#include "wui/interactive_player.h"
+#include "wui/minimap.h"
+#include "wui/mapviewpixelconstants.h"
+#include "wui/mapviewpixelfunctions.h"
+
+#include <memory>
namespace Widelands {
@@ -32,6 +41,7 @@
// a savegame without interactive player
#define CURRENT_PACKET_VERSION 4
#define PLAYERS_AMOUNT_KEY_V4 "player_amount"
+#define MINIMAP_FILENAME "minimap.png"
void Game_Preload_Data_Packet::Read
@@ -84,6 +94,9 @@
} else {
m_number_of_players = s.get_safe_int(PLAYERS_AMOUNT_KEY_V4);
}
+ if (fs.FileExists(MINIMAP_FILENAME)) {
+ m_minimap_path = fs.FS_CanonicalizeName(MINIMAP_FILENAME);
+ }
} else {
throw game_data_error
(_("unknown/unhandled version %i"), packet_version);
@@ -130,6 +143,42 @@
s.set_string("win_condition", game.get_win_condition_displayname());
prof.write("preload", false, fs);
+
+ // Write minimap image
+ if (!game.is_loaded()) {
+ return;
+ }
+ MiniMapRenderer mmr;
+ uint32_t flags = MiniMap::Owner;
+ flags |= MiniMap::Bldns;
+ flags |= MiniMap::Terrn;
+ // map dimension
+ int16_t map_w = game.get_map()->get_width();
+ int16_t map_h = game.get_map()->get_height();
+ const int32_t maxx = MapviewPixelFunctions::get_map_end_screen_x(map);
+ const int32_t maxy = MapviewPixelFunctions::get_map_end_screen_y(map);
+ // viewpt topleft in pixels
+ Point vp = ipl->get_viewpoint();
+ Point viewpt(vp.x, vp.y);
+ viewpt.x += ipl->get_w() >> 1;
+ if (viewpt.x >= maxx)
+ viewpt.x -= maxx;
+ viewpt.y += ipl->get_h() >> 1;
+ if (viewpt.y >= maxy)
+ viewpt.y -= maxy;
+ viewpt.x /= TRIANGLE_WIDTH;
+ viewpt.y /= TRIANGLE_HEIGHT;
+ viewpt.x -= map_w / 2;
+ viewpt.y -= map_h / 2;
+ // render minimap
+ std::unique_ptr<Surface> surface = mmr.get_minimap_image
+ (ipl->egbase(), &ipl->player(), Rect(0, 0, map_w, map_h), viewpt, flags);
+ const Image* im = new_in_memory_image("minimap", surface.get());
+ ::StreamWrite* sw = fs.OpenStreamWrite(MINIMAP_FILENAME);
+ g_gr->save_png(im, sw);
+ delete sw;
+ delete im;
+ surface.release();
}
}
=== modified file 'src/game_io/game_preload_data_packet.h'
--- src/game_io/game_preload_data_packet.h 2013-07-16 15:08:43 +0000
+++ src/game_io/game_preload_data_packet.h 2013-07-25 14:01:30 +0000
@@ -42,8 +42,10 @@
uint8_t get_player_nr() {return m_player_nr;}
uint8_t get_number_of_players() {return m_number_of_players;}
+ std::string get_minimap_path() {return m_minimap_path;}
private:
+ std::string m_minimap_path;
std::string m_mapname;
std::string m_background;
std::string m_win_condition;
=== modified file 'src/graphic/in_memory_image.cc'
--- src/graphic/in_memory_image.cc 2013-07-21 08:07:18 +0000
+++ src/graphic/in_memory_image.cc 2013-07-25 14:01:30 +0000
@@ -40,7 +40,9 @@
public:
InMemoryImage(const string& ghash, Surface* surf) :
hash_(ghash), surf_(surf) {}
- virtual ~InMemoryImage() {}
+ virtual ~InMemoryImage() {
+ surf_.release();
+ }
// Implements Image.
virtual uint16_t width() const {return surf_->width();}
=== modified file 'src/graphic/render/gl_surface_screen.cc'
--- src/graphic/render/gl_surface_screen.cc 2013-07-24 11:29:00 +0000
+++ src/graphic/render/gl_surface_screen.cc 2013-07-25 14:01:30 +0000
@@ -58,6 +58,12 @@
m_pixels.reset(new uint8_t[m_w * m_h * 4]);
if (mode == Lock_Normal) {
+ // FIXME: terrain dither picture somehow leave the alpha
+ // channel with non-1 values, so it is cleared before
+ // accessing pixels.
+ glColorMask(false, false, false, true);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glColorMask(true, true, true, true);
glReadPixels(0, 0, m_w, m_h, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels.get());
swap_rows();
}
=== modified file 'src/graphic/render/minimaprenderer.cc'
--- src/graphic/render/minimaprenderer.cc 2013-07-21 08:07:18 +0000
+++ src/graphic/render/minimaprenderer.cc 2013-07-25 14:01:30 +0000
@@ -34,33 +34,24 @@
#include "graphic/rendertarget.h"
#include "graphic/surface.h"
#include "graphic/texture.h"
-
+#include "graphic/image.h"
+#include "graphic/in_memory_image.h"
#include "wui/overlay_manager.h"
#include "terrain_sdl.h"
+#include <memory>
using namespace Widelands;
-/**
- * Renders a minimap into the current window. The field at viewpoint will be
- * in the top-left corner of the window. Flags specifies what information to
- * display (see Minimap_XXX enums).
- *
- * Calculate the field at the top-left corner of the clipping rect
- * The entire clipping rect will be used for drawing.
- */
-void MiniMapRenderer::renderminimap
- (const Widelands::Editor_Game_Base & egbase,
- Player const * const player,
- Point const viewpoint,
- uint32_t const flags)
+std::unique_ptr<Surface> MiniMapRenderer::get_minimap_image
+ (const Editor_Game_Base& egbase, const Player* player, Rect rect, Point viewpoint, uint32_t flags)
{
- draw_minimap
- (egbase, player, m_rect, viewpoint -
- ((flags & MiniMap::Zoom2) ? Point(m_offset.x / 2, m_offset.y / 2) : m_offset),
- viewpoint, flags);
+ std::unique_ptr<Surface> minimap_surf = draw_minimap
+ (egbase, player, rect, viewpoint, flags);
+ return minimap_surf;
}
+
/*
* Blend to colors; only needed for calc_minimap_color below
*/
@@ -223,7 +214,6 @@
Widelands::Player const * const player,
Rect const rc,
Point const viewpoint,
- Point const framepoint,
uint32_t const flags)
{
const Widelands::Map & map = egbase.map();
@@ -234,16 +224,20 @@
int32_t xsize = g_gr->get_xres() / TRIANGLE_WIDTH / 2;
int32_t ysize = g_gr->get_yres() / TRIANGLE_HEIGHT / 2;
+ // Framepoint
+ Point frame_point = viewpoint -
+ ((flags & MiniMap::Zoom2) ? Point(rc.x / 2, rc.y / 2) : Point(rc.x, rc.y));
+
Point ptopleft; // top left point of the current display frame
- ptopleft.x = framepoint.x + mapwidth / 2 - xsize;
+ ptopleft.x = frame_point.x + mapwidth / 2 - xsize;
if (ptopleft.x < 0) ptopleft.x += mapwidth;
- ptopleft.y = framepoint.y + mapheight / 2 - ysize;
+ ptopleft.y = frame_point.y + mapwidth / 2 - ysize;
if (ptopleft.y < 0) ptopleft.y += mapheight;
Point pbottomright; // bottom right point of the current display frame
- pbottomright.x = framepoint.x + mapwidth / 2 + xsize;
+ pbottomright.x = frame_point.x + mapwidth / 2 + xsize;
if (pbottomright.x >= mapwidth) pbottomright.x -= mapwidth;
- pbottomright.y = framepoint.y + mapheight / 2 + ysize;
+ pbottomright.y = frame_point.y + mapheight / 2 + ysize;
if (pbottomright.y >= mapheight) pbottomright.y -= mapheight;
uint32_t modx = pbottomright.x % 2;
@@ -310,7 +304,7 @@
player_field.owner,
1 < vision)
:
- 0);
+ SDL_MapRGB(&const_cast<SDL_PixelFormat &>(format), 0, 0, 0));
}
}
}
@@ -324,12 +318,11 @@
viewpt is the field at the top left of the rectangle.
===============
*/
-void MiniMapRenderer::draw_minimap
+std::unique_ptr<Surface> MiniMapRenderer::draw_minimap
(const Widelands::Editor_Game_Base & egbase,
Widelands::Player const * const player,
Rect const rc,
Point const viewpt,
- Point const framept,
uint32_t const flags)
{
// First create a temporary SDL Surface to draw the minimap. This Surface is
@@ -338,47 +331,31 @@
// TODO: Currently the minimap is redrawn every frame. That is not really
// necesary. The created surface could be cached and only redrawn two
// or three times per second
- const SDL_PixelFormat & fmt =
- g_gr->get_render_target()->get_surface()->format();
- SDL_Surface * surface =
- SDL_CreateRGBSurface
- (SDL_SWSURFACE,
- rc.w,
- rc.h,
- fmt.BytesPerPixel == 2 ? 16 : 32,
- fmt.Rmask,
- fmt.Gmask,
- fmt.Bmask,
- 0);
-
- Rect rc2;
- rc2.x = rc2.y = 0;
- rc2.w = rc.w;
- rc2.h = rc.h;
-
- SDL_FillRect(surface, 0, SDL_MapRGBA(surface->format, 0, 0, 0, 255));
- SDL_LockSurface(surface);
-
- Uint8 * const pixels = static_cast<uint8_t *>(surface->pixels);
+ Surface* surface = Surface::create(rc.w, rc.h);
+
+ surface->fill_rect(rc, RGBAColor(0, 0, 0, 255));
+ surface->lock(Surface::Lock_Normal);
+
+ Uint8 * const pixels = static_cast<uint8_t *>(surface->get_pixels());
Widelands::X_Coordinate const w = egbase.map().get_width();
- switch (surface->format->BytesPerPixel) {
+ switch (surface->format().BytesPerPixel) {
case sizeof(Uint16):
draw_minimap_int<Uint16>
- (pixels, surface->pitch, *surface->format,
- w, egbase, player, rc2, viewpt, framept, flags);
+ (pixels, surface->get_pitch(), surface->format(),
+ w, egbase, player, rc, viewpt, flags);
break;
case sizeof(Uint32):
draw_minimap_int<Uint32>
- (pixels, surface->pitch, *surface->format,
- w, egbase, player, rc2, viewpt, framept, flags);
+ (pixels, surface->get_pitch(), surface->format(),
+ w, egbase, player, rc, viewpt, flags);
break;
default:
assert(false);
break;
}
- SDL_UnlockSurface(surface);
+ surface->unlock(Surface::Unlock_Update);
- std::unique_ptr<Surface> minimap_surface(Surface::create(surface));
- m_surface->blit(Point(rc.x, rc.y), minimap_surface.get(), rc2);
+ std::unique_ptr<Surface> minimap_surface(surface);
+ return minimap_surface;
}
=== modified file 'src/graphic/render/minimaprenderer.h'
--- src/graphic/render/minimaprenderer.h 2013-02-10 16:41:12 +0000
+++ src/graphic/render/minimaprenderer.h 2013-07-25 14:01:30 +0000
@@ -21,6 +21,7 @@
#define WIDELANDS_MINIMAPRENDERER_H
#include "graphic/rendertarget.h"
+#include <memory>
namespace Widelands {
struct Player;
@@ -30,32 +31,30 @@
/**
* This class renders the minimap.
*/
-class MiniMapRenderer : public RenderTarget
+class MiniMapRenderer
{
public:
- MiniMapRenderer(RenderTarget & rt) :
- RenderTarget(rt) {}
+ MiniMapRenderer() {}
virtual ~MiniMapRenderer() {}
/**
* Render the minimap. If player is not 0, it renders from that player's
* point of view.
*/
- void renderminimap
+ std::unique_ptr<Surface> get_minimap_image
(const Widelands::Editor_Game_Base & egbase,
Widelands::Player const * player,
+ Rect rect,
Point viewpoint,
uint32_t flags);
-
protected:
/// A helper function to draw the minimap. This is called from
/// renderminimap().
- void draw_minimap
+ std::unique_ptr<Surface> draw_minimap
(const Widelands::Editor_Game_Base &,
Widelands::Player const *,
- Rect rc,
+ Rect rect,
Point viewpt,
- Point framept,
uint32_t flags);
};
=== modified file 'src/ui_basic/icon.cc'
--- src/ui_basic/icon.cc 2013-02-09 23:18:23 +0000
+++ src/ui_basic/icon.cc 2013-07-25 14:01:30 +0000
@@ -43,7 +43,9 @@
}
void Icon::draw(RenderTarget & dst) {
- assert(m_pic);
+ if (!m_pic) {
+ return;
+ }
int32_t w = (m_w - m_pic->width()) / 2;
int32_t h = (m_h - m_pic->height()) / 2;
dst.blit(Point(w, h), m_pic);
=== modified file 'src/ui_basic/listselect.cc'
--- src/ui_basic/listselect.cc 2013-07-21 08:25:22 +0000
+++ src/ui_basic/listselect.cc 2013-07-25 14:01:30 +0000
@@ -78,8 +78,10 @@
if (pic_h > m_lineheight)
m_lineheight = pic_h;
}
- else
+ else {
m_max_pic_width = 0;
+ }
+ set_can_focus(true);
}
=== modified file 'src/ui_basic/panel.cc'
--- src/ui_basic/panel.cc 2013-07-21 08:25:22 +0000
+++ src/ui_basic/panel.cc 2013-07-25 14:01:30 +0000
@@ -758,8 +758,9 @@
// can't. but focus is called recursivly
// assert(get_can_focus());
- if (!_parent || this == _modal)
+ if (!_parent || this == _modal) {
return;
+ }
if (_parent->_focus == this)
return;
@@ -947,9 +948,12 @@
* Returns whether the event was processed.
*/
bool Panel::do_mousepress(const Uint8 btn, int32_t x, int32_t y) {
- if (not _g_allow_user_input)
+ if (not _g_allow_user_input) {
return true;
-
+ }
+ if (get_can_focus()) {
+ focus();
+ }
x -= _lborder;
y -= _tborder;
if (_flags & pf_top_on_click)
@@ -971,8 +975,10 @@
(Panel * child = _fchild;
(child = child_at_mouse_cursor(x, y, child));
child = child->_next)
- if (child->do_mousepress(btn, x - child->_x, y - child->_y))
- return true;
+ {
+ if (child->do_mousepress(btn, x - child->_x, y - child->_y))
+ return true;
+ }
return handle_mousepress(btn, x, y);
}
bool Panel::do_mouserelease(const Uint8 btn, int32_t x, int32_t y) {
=== modified file 'src/ui_basic/table.cc'
--- src/ui_basic/table.cc 2013-07-21 14:36:52 +0000
+++ src/ui_basic/table.cc 2013-07-25 14:01:30 +0000
@@ -63,6 +63,7 @@
m_sort_descending (descending)
{
set_think(false);
+ set_can_focus(true);
}
=== modified file 'src/ui_basic/window.cc'
--- src/ui_basic/window.cc 2013-07-20 11:02:27 +0000
+++ src/ui_basic/window.cc 2013-07-25 14:01:30 +0000
@@ -433,9 +433,6 @@
*/
bool Window::handle_mousepress(const Uint8 btn, int32_t mx, int32_t my)
{
- if (get_can_focus())
- focus();
-
// FIXME This code is erroneous. It checks the current key state. What it
// FIXME needs is the key state at the time the mouse was clicked. See the
// FIXME usage comment for get_key_state.
=== modified file 'src/ui_fsmenu/loadgame.cc'
--- src/ui_fsmenu/loadgame.cc 2013-07-19 11:00:01 +0000
+++ src/ui_fsmenu/loadgame.cc 2013-07-25 14:01:30 +0000
@@ -24,11 +24,15 @@
#include "gamecontroller.h"
#include "gamesettings.h"
#include "graphic/graphic.h"
+#include "graphic/image_loader_impl.h"
+#include "graphic/image_transformations.h"
+#include "graphic/in_memory_image.h"
#include "i18n.h"
#include "io/filesystem/layered_filesystem.h"
#include "log.h"
#include "logic/game.h"
#include "ui_basic/messagebox.h"
+#include "ui_basic/icon.h"
#include "timestring.h"
#include <cstdio>
@@ -91,7 +95,9 @@
(this, get_w() * 71 / 100, get_h() * 41 / 100),
m_ta_win_condition
(this, get_w() * 71 / 100, get_h() * 9 / 20),
-
+ m_minimap_icon
+ (this, get_w() * 7 / 10, get_h() * 10 / 20,
+ get_w() * 15 / 100, get_w() * 15 / 100, nullptr),
m_settings(gsp),
m_ctrl(gc)
{
@@ -161,6 +167,9 @@
m_tamapname .set_text(std::string());
m_tagametime.set_text(std::string());
+ m_ta_players.set_text(std::string());
+ m_ta_win_condition.set_text(std::string());
+ m_minimap_icon.setIcon(nullptr);
}
@@ -215,6 +224,33 @@
}
m_ta_players.set_text(buf);
m_ta_win_condition.set_text(gpdp.get_win_condition());
+
+ std::string minimap_path = gpdp.get_minimap_path();
+ // Delete former image
+ m_minimap_icon.setIcon(nullptr);
+ if (m_minimap_image) {
+ m_minimap_image.release();
+ }
+ // Load the new one
+ if (!minimap_path.empty()) {
+ try {
+ ImageLoaderImpl* ill = new ImageLoaderImpl();
+ Surface * surf = ill->load(minimap_path);
+ const Image* im = new_in_memory_image("minimap", surf);
+ double scale = double(m_minimap_icon.get_w()) / im->width();
+ double scaleY = double(m_minimap_icon.get_h()) / im->height();
+ if (scaleY < scale) {
+ scale = scaleY;
+ }
+ uint16_t w = scale * im->width();
+ uint16_t h = scale * im->height();
+ const Image* resized = ImageTransformations::resize
+ (im, w, h);
+ m_minimap_image.reset(resized);
+ m_minimap_icon.setIcon(m_minimap_image.get());
+ } catch (...) {
+ }
+ }
} else {
no_selection();
}
=== modified file 'src/ui_fsmenu/loadgame.h'
--- src/ui_fsmenu/loadgame.h 2013-07-13 16:16:24 +0000
+++ src/ui_fsmenu/loadgame.h 2013-07-25 14:01:30 +0000
@@ -26,6 +26,7 @@
#include "ui_basic/button.h"
#include "ui_basic/checkbox.h"
+#include "ui_basic/icon.h"
#include "ui_basic/listselect.h"
#include "ui_basic/multilinetextarea.h"
#include "ui_basic/textarea.h"
@@ -36,6 +37,7 @@
struct Map;
struct Map_Loader;
};
+class Image;
class RenderTarget;
struct GameController;
struct GameSettingsProvider;
@@ -76,12 +78,14 @@
UI::Textarea m_label_players;
UI::Textarea m_ta_players;
UI::Textarea m_ta_win_condition;
+ UI::Icon m_minimap_icon;
std::string m_filename;
filenameset_t m_gamefiles;
GameSettingsProvider * m_settings;
GameController * m_ctrl;
+ std::unique_ptr<const Image> m_minimap_image;
};
=== modified file 'src/ui_fsmenu/mapselect.cc'
--- src/ui_fsmenu/mapselect.cc 2013-07-20 10:15:23 +0000
+++ src/ui_fsmenu/mapselect.cc 2013-07-25 14:01:30 +0000
@@ -191,6 +191,7 @@
m_label_load_map_as_scenario.set_visible(false);
}
+ m_table.focus();
fill_list();
}
@@ -200,6 +201,7 @@
m_ctrl->think();
}
+
bool Fullscreen_Menu_MapSelect::compare_maprows
(uint32_t rowa, uint32_t rowb)
{
=== modified file 'src/wui/minimap.cc'
--- src/wui/minimap.cc 2013-02-10 19:36:24 +0000
+++ src/wui/minimap.cc 2013-07-25 14:01:30 +0000
@@ -24,7 +24,9 @@
#include "logic/map.h"
#include "mapviewpixelconstants.h"
+#include "graphic/surface.h"
#include "graphic/graphic.h"
+#include "graphic/in_memory_image.h"
#include "graphic/rendertarget.h"
#include "graphic/render/minimaprenderer.h"
@@ -60,15 +62,20 @@
void MiniMap::View::draw(RenderTarget & dst)
{
- MiniMapRenderer mmr(dst);
+ MiniMapRenderer mmr;
- mmr.renderminimap
+ std::unique_ptr<Surface> surface = mmr.get_minimap_image
(m_ibase.egbase(),
m_ibase.get_player(),
+ Rect(0, 0, dst.get_rect().w, dst.get_rect().h),
(*m_flags) & (MiniMap::Zoom2) ?
Point((m_viewx - get_w() / 4), (m_viewy - get_h() / 4)):
Point((m_viewx - get_w() / 2), (m_viewy - get_h() / 2)),
*m_flags);
+ const Image* im = new_in_memory_image("minimap", surface.get());
+ dst.blit(Point(), im);
+ delete im;
+ surface.release();
}
Follow ups