← Back to team overview

widelands-dev team mailing list archive

[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