← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/render_queue into lp:widelands

 

SirVer has proposed merging lp:~widelands-dev/widelands/render_queue into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/render_queue/+merge/250524

Okay, here is a mouthful. I apologize for not better splitting this up, but it came together over a bunch of experiments and it is not even quite done. Here it goes:

Suggested commit message:
- Implemented a RenderQueue. All drawing operations during drawing only
  register an item into the queue, at the end of the frame, the RenderQueue
  will draw everything onto the screen all at once. This leads to significant
  less OpenGL state changes, but will only make a really big difference when
  all graphics land in texture atlases.
- Textures internal format is now OpenGL ordered (i.e. bottom line is y = 0).
  This needs swapping of rows for pictures on load, but Textures are no longer
  upside down on the GPU which makes working with OpenGL coordinates easier.
- Made all drawing programs z-layer aware and enabled depth testing for OpenGL.
- Made all programs batch aware: The renderqueue batches items for the same
  program into a vector which is passed to the programs. They can then batch
  them together into fewer draw calls if possible.
- An ImageCache now has a compactify() method that builds a texture atlas out
  of all the image that are cached in it while having all Image pointers it
  handed out before staying valid. This was only used for debugging, but in a
  followup branch I will change the ImageTexture to be backed by a texture
  atlas by default.
- Refactored minimap renderer to have less repeated code. 
- Remove Texture::get_pixels() and format() and let the (set|get)_pixels pass
  RGBAColor instead of uint32_t for clr.
- RenderTarget::draw_line() and derived methods now take two Points instead of
  4 integers which are inclusive to the line.
- Only reallocate OpenGL buffers if we need more space.
- Small refactorings in src/graphics/CMakeLists.txt.

-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/render_queue into lp:widelands.
=== modified file 'src/base/rect.h'
--- src/base/rect.h	2014-11-26 19:53:52 +0000
+++ src/base/rect.h	2015-02-21 07:13:48 +0000
@@ -36,13 +36,13 @@
 	   : GenericRect(T(p.x), T(p.y), width, height) {
 	}
 
-	/// The top left point of this rectangle.
-	GenericPoint<T> top_left() const {
+	/// The Point (x, y).
+	GenericPoint<T> origin() const {
 		return GenericPoint<T>(x, y);
 	}
 
-	/// The bottom right point of this rectangle.
-	GenericPoint<T> bottom_right() const {
+	/// The point (x + w, y + h).
+	GenericPoint<T> opposite_of_origin() const {
 		return GenericPoint<T>(x + w, y + h);
 	}
 

=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc	2015-01-31 16:03:59 +0000
+++ src/editor/editorinteractive.cc	2015-02-21 07:13:48 +0000
@@ -604,6 +604,7 @@
 
 		eia.select_tool(eia.tools.increase_height, EditorTool::First);
 		editor.postload();
+
 		eia.start();
 
 		if (!script_to_run.empty()) {

=== modified file 'src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc'
--- src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc	2015-02-16 13:10:35 +0000
+++ src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc	2015-02-21 07:13:48 +0000
@@ -75,75 +75,69 @@
 
 		const Texture& terrain_texture = terrain_descr.get_texture(0);
 		Texture* texture = new Texture(terrain_texture.width(), terrain_texture.height());
-		blit(Rect(0, 0, terrain_texture.width(), terrain_texture.height()),
+		texture->blit(Rect(0, 0, terrain_texture.width(), terrain_texture.height()),
 		              terrain_texture,
 		              Rect(0, 0, terrain_texture.width(), terrain_texture.height()),
 		              1.,
-		              BlendMode::UseAlpha, texture);
+		              BlendMode::UseAlpha);
 		Point pt(1, terrain_texture.height() - kSmallPicHeight - 1);
 
 		if (ter_is == TerrainDescription::Type::kGreen) {
-			blit(Rect(pt.x, pt.y, green->width(), green->height()),
-			     *green,
-			     Rect(0, 0, green->width(), green->height()),
-			     1.,
-			     BlendMode::UseAlpha,
-			     texture);
+			texture->blit(Rect(pt.x, pt.y, green->width(), green->height()),
+			              *green,
+			              Rect(0, 0, green->width(), green->height()),
+			              1.,
+			              BlendMode::UseAlpha);
 			pt.x += kSmallPicWidth + 1;
 			/** TRANSLATORS: This is a terrain type tooltip in the editor */
 			tooltips.push_back(_("arable"));
 		} else {
 			if (ter_is & TerrainDescription::Type::kWater) {
-				blit(Rect(pt.x, pt.y, water->width(), water->height()),
-				     *water,
-				     Rect(0, 0, water->width(), water->height()),
-				     1.,
-				     BlendMode::UseAlpha,
-				     texture);
+				texture->blit(Rect(pt.x, pt.y, water->width(), water->height()),
+				              *water,
+				              Rect(0, 0, water->width(), water->height()),
+				              1.,
+				              BlendMode::UseAlpha);
 				pt.x += kSmallPicWidth + 1;
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				tooltips.push_back(_("aquatic"));
 			}
 			else if (ter_is & TerrainDescription::Type::kMountain) {
-				blit(Rect(pt.x, pt.y, mountain->width(), mountain->height()),
-				     *mountain,
-				     Rect(0, 0, mountain->width(), mountain->height()),
-				     1.,
-				     BlendMode::UseAlpha,
-				     texture);
+				texture->blit(Rect(pt.x, pt.y, mountain->width(), mountain->height()),
+				              *mountain,
+				              Rect(0, 0, mountain->width(), mountain->height()),
+				              1.,
+				              BlendMode::UseAlpha);
 				pt.x += kSmallPicWidth + 1;
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				tooltips.push_back(_("mountainous"));
 			}
 			if (ter_is & TerrainDescription::Type::kDead) {
-				blit(Rect(pt.x, pt.y, dead->width(), dead->height()),
-				     *dead,
-				     Rect(0, 0, dead->width(), dead->height()),
-				     1.,
-				     BlendMode::UseAlpha,
-				     texture);
+				texture->blit(Rect(pt.x, pt.y, dead->width(), dead->height()),
+				              *dead,
+				              Rect(0, 0, dead->width(), dead->height()),
+				              1.,
+				              BlendMode::UseAlpha);
 				pt.x += kSmallPicWidth + 1;
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				tooltips.push_back(_("dead"));
 			}
 			if (ter_is & TerrainDescription::Type::kUnpassable) {
-				blit(Rect(pt.x, pt.y, unpassable->width(), unpassable->height()),
-				     *unpassable,
-				     Rect(0, 0, unpassable->width(), unpassable->height()),
-				     1.,
-				     BlendMode::UseAlpha,
-				     texture);
+				texture->blit(Rect(pt.x, pt.y, unpassable->width(), unpassable->height()),
+				              *unpassable,
+				              Rect(0, 0, unpassable->width(), unpassable->height()),
+				              1.,
+				              BlendMode::UseAlpha);
 				pt.x += kSmallPicWidth + 1;
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				tooltips.push_back(_("unpassable"));
 			}
 			if (ter_is & TerrainDescription::Type::kDry) {
-				blit(Rect(pt.x, pt.y, dry->width(), dry->height()),
-				     *dry,
-				     Rect(0, 0, dry->width(), dry->height()),
-				     1.,
-				     BlendMode::UseAlpha,
-				     texture);
+				texture->blit(Rect(pt.x, pt.y, dry->width(), dry->height()),
+				              *dry,
+				              Rect(0, 0, dry->width(), dry->height()),
+				              1.,
+				              BlendMode::UseAlpha);
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				 tooltips.push_back(_("treeless"));
 			}

=== modified file 'src/graphic/CMakeLists.txt'
--- src/graphic/CMakeLists.txt	2015-02-09 05:57:08 +0000
+++ src/graphic/CMakeLists.txt	2015-02-21 07:13:48 +0000
@@ -10,6 +10,21 @@
   USES_SDL2
 )
 
+wl_library(graphic_render_queue
+  SRCS
+    render_queue.cc
+    render_queue.h
+  DEPENDS
+    base_exceptions
+    base_geometry
+    base_log
+    base_macros
+    graphic_color
+    graphic_terrain_programs
+    graphic_draw_programs
+    logic
+)
+
 wl_library(graphic_text_layout
   SRCS
     text_layout.cc
@@ -22,7 +37,6 @@
     graphic_text
 )
 
-
 wl_library(graphic_image_io
   SRCS
     image_io.h
@@ -32,6 +46,7 @@
   USES_SDL2_IMAGE
   DEPENDS
     base_exceptions
+    base_log
     graphic_surface
     io_fileread
     io_filesystem
@@ -44,10 +59,10 @@
     image_cache.h
   USES_SDL2
   DEPENDS
-    base_log
     base_macros
     graphic_image_io
     graphic_surface
+    graphic_texture_atlas
 )
 
 wl_library(graphic_sdl_utils
@@ -59,6 +74,7 @@
 
 wl_library(graphic_gl_utils
   SRCS
+    gl/coordinate_conversion.h
     gl/system_headers.h
     gl/utils.cc
     gl/utils.h
@@ -66,19 +82,54 @@
   USES_OPENGL
   DEPENDS
     base_exceptions
-    base_log
-    base_macros
-)
-
-wl_library(graphic_surface
+    base_geometry
+    base_log
+    base_macros
+)
+
+wl_library(graphic_terrain_programs
+  SRCS
+    gl/fields_to_draw.h
+    gl/road_program.cc
+    gl/road_program.h
+    gl/terrain_program.cc
+    gl/terrain_program.h
+    gl/dither_program.cc
+    gl/dither_program.h
+  DEPENDS
+    base_exceptions
+    base_geometry
+    base_log
+    base_macros
+    graphic
+    graphic_gl_utils
+    graphic_image_io
+    graphic_surface
+    io_filesystem
+    logic
+)
+
+wl_library(graphic_draw_programs
   SRCS
     blend_mode.h
     gl/blit_program.cc
     gl/blit_program.h
+    gl/blit_source.h
     gl/draw_line_program.cc
     gl/draw_line_program.h
     gl/fill_rect_program.cc
     gl/fill_rect_program.h
+  DEPENDS
+    base_exceptions
+    base_macros
+    base_geometry
+    graphic_gl_utils
+    base_log
+    graphic_color
+)
+
+wl_library(graphic_surface
+  SRCS
     image.h
     screen.cc
     screen.h
@@ -97,7 +148,9 @@
     base_macros
     graphic
     graphic_color
+    graphic_draw_programs
     graphic_gl_utils
+    graphic_render_queue
     graphic_sdl_utils
 )
 
@@ -115,23 +168,14 @@
   SRCS
     game_renderer.cc
     game_renderer.h
-    gl/dither_program.cc
-    gl/dither_program.h
-    gl/fields_to_draw.h
-    gl/road_program.cc
-    gl/road_program.h
-    gl/terrain_program.cc
-    gl/terrain_program.h
   DEPENDS
-    base_exceptions
     base_geometry
-    base_log
     base_macros
     graphic
     graphic_gl_utils
-    graphic_image_io
+    graphic_render_queue
     graphic_surface
-    io_filesystem
+    graphic_terrain_programs
     logic
     wui_mapview_pixelfunctions
     wui_overlay_manager
@@ -183,12 +227,14 @@
     base_geometry
     base_i18n
     base_log
+    graphic_draw_programs
     base_macros
     build_info
     graphic_color
     graphic_gl_utils
     graphic_image_cache
     graphic_image_io
+    graphic_render_queue
     graphic_surface
     graphic_text
     graphic_text_layout

=== modified file 'src/graphic/animation.cc'
--- src/graphic/animation.cc	2014-12-08 05:22:52 +0000
+++ src/graphic/animation.cc	2015-02-21 07:13:48 +0000
@@ -325,19 +325,11 @@
 	assert(idx < nr_frames());
 
 	if (!hasplrclrs_ || clr == nullptr) {
-		::blit(Rect(dst.x, dst.y, srcrc.w, srcrc.h),
-		     *frames_.at(idx),
-		     srcrc,
-		     1.,
-		     BlendMode::UseAlpha,
-		     target);
+		target->blit(
+		   Rect(dst.x, dst.y, srcrc.w, srcrc.h), *frames_.at(idx), srcrc, 1., BlendMode::UseAlpha);
 	} else {
-		blit_blended(Rect(dst.x, dst.y, srcrc.w, srcrc.h),
-		             *frames_.at(idx),
-		             *pcmasks_.at(idx),
-		             srcrc,
-		             *clr,
-		             target);
+		target->blit_blended(
+		   Rect(dst.x, dst.y, srcrc.w, srcrc.h), *frames_.at(idx), *pcmasks_.at(idx), srcrc, *clr);
 	}
 }
 

=== modified file 'src/graphic/blend_mode.h'
--- src/graphic/blend_mode.h	2014-12-04 09:00:20 +0000
+++ src/graphic/blend_mode.h	2015-02-21 07:13:48 +0000
@@ -24,10 +24,14 @@
 enum class BlendMode {
 	// Perform a normal blitting operation that respects the alpha channel if
 	// present.
-	UseAlpha = 0,
+	UseAlpha,
+
+	// Used internally for Surface::brighten_rect() if the rect is actually to
+	// be darkened.
+	Subtract,
 
 	// Copy all pixel information, including alpha channel information.
-	Copy
+	Copy,
 };
 
 #endif  // end of include guard: WL_GRAPHIC_BLEND_MODE_H

=== modified file 'src/graphic/color.cc'
--- src/graphic/color.cc	2014-07-20 07:44:53 +0000
+++ src/graphic/color.cc	2015-02-21 07:13:48 +0000
@@ -19,7 +19,7 @@
 
 #include "graphic/color.h"
 
-RGBColor::RGBColor() {
+RGBColor::RGBColor() : RGBColor(0, 0, 0) {
 }
 
 RGBColor::RGBColor(uint8_t const R, uint8_t const G, uint8_t const B) :

=== modified file 'src/graphic/color.h'
--- src/graphic/color.h	2014-07-14 10:45:44 +0000
+++ src/graphic/color.h	2015-02-21 07:13:48 +0000
@@ -24,6 +24,7 @@
 
 struct RGBColor {
 	RGBColor(uint8_t R, uint8_t G, uint8_t B);
+	RGBColor(const RGBColor& other) = default;
 
 	// Initializes the color to black.
 	RGBColor();
@@ -34,14 +35,16 @@
 	// Set it to the given 'clr' which is interpretes through 'fmt'.
 	void set(SDL_PixelFormat * fmt, uint32_t clr);
 
+	RGBColor& operator = (const RGBColor& other) = default;
+	bool operator != (const RGBColor& other) const;
 	bool operator == (const RGBColor& other) const;
-	bool operator != (const RGBColor& other) const;
 
 	uint8_t r, g, b;
 };
 
 struct RGBAColor {
 	RGBAColor(uint8_t R, uint8_t G, uint8_t B, uint8_t A);
+	RGBAColor(const RGBAColor& other) = default;
 
 	// Initializes the color to black.
 	RGBAColor();
@@ -55,8 +58,9 @@
 	// Set it to the given 'clr' which is interpretes through 'fmt'.
 	void set(const SDL_PixelFormat & fmt, uint32_t clr);
 
+	RGBAColor& operator = (const RGBAColor& other) = default;
+	bool operator != (const RGBAColor& other) const;
 	bool operator == (const RGBAColor& other) const;
-	bool operator != (const RGBAColor& other) const;
 
 	uint8_t r;
 	uint8_t g;

=== modified file 'src/graphic/game_renderer.cc'
--- src/graphic/game_renderer.cc	2015-02-08 18:16:41 +0000
+++ src/graphic/game_renderer.cc	2015-02-21 07:13:48 +0000
@@ -21,11 +21,9 @@
 
 #include <memory>
 
-#include "graphic/gl/dither_program.h"
-#include "graphic/gl/fields_to_draw.h"
-#include "graphic/gl/road_program.h"
-#include "graphic/gl/terrain_program.h"
+#include "graphic/gl/coordinate_conversion.h"
 #include "graphic/graphic.h"
+#include "graphic/render_queue.h"
 #include "graphic/rendertarget.h"
 #include "graphic/surface.h"
 #include "logic/editor_game_base.h"
@@ -66,10 +64,6 @@
 // d). Example: if r and d have different textures and r.dither_layer >
 // d.dither_layer, then we will repaint d with the dither texture as mask.
 
-std::unique_ptr<TerrainProgram> GameRenderer::terrain_program_;
-std::unique_ptr<DitherProgram> GameRenderer::dither_program_;
-std::unique_ptr<RoadProgram> GameRenderer::road_program_;
-
 namespace {
 
 using namespace Widelands;
@@ -142,12 +136,6 @@
                         const EditorGameBase& egbase,
                         const Point& view_offset,
                         const Player* player) {
-	if (terrain_program_ == nullptr) {
-		terrain_program_.reset(new TerrainProgram());
-		dither_program_.reset(new DitherProgram());
-		road_program_.reset(new RoadProgram());
-	}
-
 	Point tl_map = dst.get_offset() + view_offset;
 
 	assert(tl_map.x >= 0); // divisions involving negative numbers are bad
@@ -170,22 +158,18 @@
 		return;
 
 	const Rect& bounding_rect = dst.get_rect();
-	const Point surface_offset = bounding_rect.top_left() + dst.get_offset() - view_offset;
-
-	glScissor(bounding_rect.x,
-	          surface->height() - bounding_rect.y - bounding_rect.h,
-	          bounding_rect.w,
-	          bounding_rect.h);
-	glEnable(GL_SCISSOR_TEST);
+	const Point surface_offset = bounding_rect.origin() + dst.get_offset() - view_offset;
+	const int surface_width = surface->width();
+	const int surface_height = surface->height();
 
 	Map& map = egbase.map();
 	const uint32_t gametime = egbase.get_gametime();
 
-	FieldsToDraw fields_to_draw(minfx, maxfx, minfy, maxfy);
+	fields_to_draw_.reset(minfx, maxfx, minfy, maxfy);
 	for (int32_t fy = minfy; fy <= maxfy; ++fy) {
 		for (int32_t fx = minfx; fx <= maxfx; ++fx) {
 			FieldsToDraw::Field& f =
-			   *fields_to_draw.mutable_field(fields_to_draw.calculate_index(fx, fy));
+			   *fields_to_draw_.mutable_field(fields_to_draw_.calculate_index(fx, fy));
 
 			f.fx = fx;
 			f.fy = fy;
@@ -202,7 +186,7 @@
 
 			f.gl_x = f.pixel_x = x + surface_offset.x;
 			f.gl_y = f.pixel_y = y + surface_offset.y - fcoords.field->get_height() * HEIGHT_FACTOR;
-			surface->pixel_to_gl(&f.gl_x, &f.gl_y);
+			pixel_to_gl_renderbuffer(surface_width, surface_height, &f.gl_x, &f.gl_y);
 
 			f.ter_d = fcoords.field->terrain_d();
 			f.ter_r = fcoords.field->terrain_r();
@@ -220,14 +204,32 @@
 		}
 	}
 
-	const World& world = egbase.world();
-	terrain_program_->draw(gametime, world.terrains(), fields_to_draw);
-	dither_program_->draw(gametime, world.terrains(), fields_to_draw);
-	road_program_->draw(*surface, fields_to_draw);
+	// Enqueue the drawing of the terrain.
+	RenderQueue::Item i;
+	i.program_id = RenderQueue::Program::TERRAIN_BASE;
+	i.blend_mode = BlendMode::Copy;
+	i.destination_rect =
+	   FloatRect(bounding_rect.x,
+	             surface->height() - bounding_rect.y - bounding_rect.h,
+	             bounding_rect.w,
+	             bounding_rect.h);
+	i.terrain_arguments.gametime = gametime;
+	i.terrain_arguments.renderbuffer_width = surface->width();
+	i.terrain_arguments.renderbuffer_height = surface->height();
+	i.terrain_arguments.terrains = &egbase.world().terrains();
+	i.terrain_arguments.fields_to_draw = &fields_to_draw_;
+	RenderQueue::instance().enqueue(i);
+
+	// Enqueue the drawing of the dither layer.
+	i.program_id = RenderQueue::Program::TERRAIN_DITHER;
+	i.blend_mode = BlendMode::UseAlpha;
+	RenderQueue::instance().enqueue(i);
+
+	// Enqueue the drawing of the road layer.
+	i.program_id = RenderQueue::Program::TERRAIN_ROAD;
+	RenderQueue::instance().enqueue(i);
 
 	draw_objects(dst, egbase, view_offset, player, minfx, maxfx, minfy, maxfy);
-
-	glDisable(GL_SCISSOR_TEST);
 }
 
 void GameRenderer::draw_objects(RenderTarget& dst,

=== modified file 'src/graphic/game_renderer.h'
--- src/graphic/game_renderer.h	2014-12-04 21:21:05 +0000
+++ src/graphic/game_renderer.h	2015-02-21 07:13:48 +0000
@@ -24,17 +24,14 @@
 
 #include "base/macros.h"
 #include "base/point.h"
+#include "graphic/gl/fields_to_draw.h"
 
 namespace Widelands {
 	class Player;
 	class EditorGameBase;
 }
 
-class DitherProgram;
 class RenderTarget;
-class RoadProgram;
-class TerrainProgram;
-
 
 /**
  * This abstract base class renders the main game view into an
@@ -64,10 +61,6 @@
 	void rendermap(RenderTarget& dst, const Widelands::EditorGameBase& egbase, const Point& view_offset);
 
 private:
-	static std::unique_ptr<TerrainProgram> terrain_program_;
-	static std::unique_ptr<DitherProgram> dither_program_;
-	static std::unique_ptr<RoadProgram> road_program_;
-
 	// Draw the map for the given parameters (see rendermap). 'player'
 	// can be nullptr in which case the whole map is drawn.
 	void draw(RenderTarget& dst,
@@ -85,6 +78,10 @@
 	                  int minfy,
 	                  int maxfy);
 
+	// This is owned and handled by us, but handed to the RenderQueue, so we
+	// basically promise that this stays valid for one frame.
+	FieldsToDraw fields_to_draw_;
+
 	DISALLOW_COPY_AND_ASSIGN(GameRenderer);
 };
 

=== modified file 'src/graphic/gl/blit_program.cc'
--- src/graphic/gl/blit_program.cc	2014-12-06 15:32:50 +0000
+++ src/graphic/gl/blit_program.cc	2015-02-21 07:13:48 +0000
@@ -30,42 +30,44 @@
 #version 120
 
 // Attributes.
-attribute vec2 attr_position;
-
-// Uniforms.
-uniform vec4 u_dst_rect;
-uniform vec4 u_src_rect;
-
+attribute vec2 attr_mask_texture_position;
+attribute vec2 attr_texture_position;
+attribute vec3 attr_position;
+attribute vec4 attr_blend;
+
+varying vec2 out_mask_texture_coordinate;
 varying vec2 out_texture_coordinate;
+varying vec4 out_blend;
 
 void main() {
-	out_texture_coordinate = u_src_rect.xy + attr_position.xy * u_src_rect.zw;
-	gl_Position = vec4(u_dst_rect.xy + attr_position.xy * u_dst_rect.zw, 0., 1.);
+	out_mask_texture_coordinate = attr_mask_texture_position;
+	out_texture_coordinate = attr_texture_position;
+	out_blend = attr_blend;
+	gl_Position = vec4(attr_position, 1.);
 }
 )";
 
 const char kVanillaBlitFragmentShader[] = R"(
 #version 120
 
-uniform float u_opacity;
 uniform sampler2D u_texture;
 
 varying vec2 out_texture_coordinate;
+varying vec4 out_blend;
 
 void main() {
 	vec4 color = texture2D(u_texture, out_texture_coordinate);
-	gl_FragColor = vec4(color.rgb, u_opacity * color.a);
+	gl_FragColor = color * out_blend;
 }
 )";
 
 const char kMonochromeBlitFragmentShader[] = R"(
 #version 120
 
-uniform float u_opacity;
 uniform sampler2D u_texture;
-uniform vec3 u_blend;
 
 varying vec2 out_texture_coordinate;
+varying vec4 out_blend;
 
 void main() {
 	vec4 texture_color = texture2D(u_texture, out_texture_coordinate);
@@ -73,144 +75,270 @@
 	// See http://en.wikipedia.org/wiki/YUV.
 	float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);
 
-	gl_FragColor = vec4(vec3(luminance) * u_blend, u_opacity * texture_color.a);
+	gl_FragColor = vec4(vec3(luminance) * out_blend.rgb, out_blend.a * texture_color.a);
 }
 )";
 
 const char kBlendedBlitFragmentShader[] = R"(
 #version 120
 
-uniform float u_opacity;
 uniform sampler2D u_texture;
 uniform sampler2D u_mask;
-uniform vec3 u_blend;
 
+varying vec2 out_mask_texture_coordinate;
 varying vec2 out_texture_coordinate;
+varying vec4 out_blend;
 
 void main() {
 	vec4 texture_color = texture2D(u_texture, out_texture_coordinate);
-	vec4 mask_color = texture2D(u_mask, out_texture_coordinate);
+	vec4 mask_color = texture2D(u_mask, out_mask_texture_coordinate);
 
 	// See http://en.wikipedia.org/wiki/YUV.
 	float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);
 	float blend_influence = mask_color.r * mask_color.a;
 	gl_FragColor = vec4(
-	   mix(texture_color.rgb, u_blend * luminance, blend_influence), u_opacity * texture_color.a);
+		mix(texture_color.rgb, out_blend.rgb * luminance, blend_influence), out_blend.a * texture_color.a);
 }
 )";
 
+// While drawing we put all draw calls into a buffer, so that we have to
+// transfer the buffer to the GPU only once, even though we might need to do
+// many glDraw* calls. This structure represents the parameters for one glDraw*
+// call.
+struct DrawBatch {
+	int offset;
+	int count;
+	int texture;
+	int mask;
+	BlendMode blend_mode;
+};
+
 }  // namespace
 
 class BlitProgram {
 public:
+	struct Arguments {
+		FloatRect destination_rect;
+		float z_value;
+		BlitSource texture;
+		BlitSource mask;
+		RGBAColor blend;
+		BlendMode blend_mode;
+	};
 	BlitProgram(const std::string& fragment_shader);
 
-	void activate(const FloatRect& gl_dest_rect,
-	              const FloatRect& gl_src_rect,
-	              const GLuint gl_texture,
-					  const float opacity,
-	              const BlendMode blend_mode);
-
-	void draw();
-	void draw_and_deactivate(BlendMode blend_mode);
-
-	GLuint program_object() const {
+	void activate();
+
+	void draw_and_deactivate(const std::vector<Arguments>& arguments);
+
+	int program_object() const {
 		return gl_program_.object();
 	}
 
 private:
 	struct PerVertexData {
-		float gl_x, gl_y;
+		PerVertexData(float init_gl_x,
+		              float init_gl_y,
+		              float init_gl_z,
+		              float init_texture_x,
+		              float init_texture_y,
+		              float init_mask_texture_x,
+		              float init_mask_texture_y,
+		              float init_blend_r,
+		              float init_blend_g,
+		              float init_blend_b,
+		              float init_blend_a)
+		   : gl_x(init_gl_x),
+		     gl_y(init_gl_y),
+		     gl_z(init_gl_z),
+		     texture_x(init_texture_x),
+		     texture_y(init_texture_y),
+		     mask_texture_x(init_mask_texture_x),
+		     mask_texture_y(init_mask_texture_y),
+		     blend_r(init_blend_r),
+		     blend_g(init_blend_g),
+		     blend_b(init_blend_b),
+		     blend_a(init_blend_a) {
+		}
+
+		float gl_x, gl_y, gl_z;
+		float texture_x, texture_y;
+		float mask_texture_x, mask_texture_y;
+		float blend_r, blend_g, blend_b, blend_a;
 	};
-	static_assert(sizeof(PerVertexData) == 8, "Wrong padding.");
+	static_assert(sizeof(PerVertexData) == 44, "Wrong padding.");
 
 	// The buffer that will contain the quad for rendering.
-	Gl::Buffer gl_array_buffer_;
+	Gl::NewBuffer<PerVertexData> gl_array_buffer_;
 
 	// The program.
 	Gl::Program gl_program_;
 
 	// Attributes.
+	GLint attr_blend_;
+	GLint attr_mask_texture_position_;
 	GLint attr_position_;
+	GLint attr_texture_position_;
 
 	// Uniforms.
-	GLint u_dst_rect_;
-	GLint u_opacity_;
-	GLint u_src_rect_;
 	GLint u_texture_;
+	GLint u_mask_;
+
+	// Cached for efficiency.
+	std::vector<PerVertexData> vertices_;
+
 	DISALLOW_COPY_AND_ASSIGN(BlitProgram);
 };
 
 BlitProgram::BlitProgram(const std::string& fragment_shader) {
 	gl_program_.build(kBlitVertexShader, fragment_shader.c_str());
 
+	attr_blend_ = glGetAttribLocation(gl_program_.object(), "attr_blend");
+	attr_mask_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_mask_texture_position");
 	attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");
+	attr_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_texture_position");
 
 	u_texture_ = glGetUniformLocation(gl_program_.object(), "u_texture");
-	u_opacity_ = glGetUniformLocation(gl_program_.object(), "u_opacity");
-	u_dst_rect_ = glGetUniformLocation(gl_program_.object(), "u_dst_rect");
-	u_src_rect_ = glGetUniformLocation(gl_program_.object(), "u_src_rect");
-
-	std::vector<PerVertexData> vertices;
-	vertices.push_back(PerVertexData
-			{0., 1.});
-	vertices.push_back(PerVertexData
-			{1., 1.});
-	vertices.push_back(PerVertexData
-			{0., 0.});
-	vertices.push_back(PerVertexData
-			{1., 0.});
-
-	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
-	glBufferData(
-	   GL_ARRAY_BUFFER, sizeof(PerVertexData) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
-	glVertexAttribPointer(attr_position_,
-								 2,
-								 GL_FLOAT,
-								 GL_FALSE,
-								 sizeof(PerVertexData),
-								 reinterpret_cast<void*>(0));
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
+	u_mask_ = glGetUniformLocation(gl_program_.object(), "u_mask");
 }
 
-void BlitProgram::activate(const FloatRect& gl_dest_rect,
-                       const FloatRect& gl_src_rect,
-                       const GLuint gl_texture,
-							  const float opacity,
-                       const BlendMode blend_mode) {
+void BlitProgram::activate() {
 	glUseProgram(gl_program_.object());
+
+	glEnableVertexAttribArray(attr_blend_);
+	glEnableVertexAttribArray(attr_mask_texture_position_);
 	glEnableVertexAttribArray(attr_position_);
-	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
-
-	glVertexAttribPointer(attr_position_,
-								 2,
-								 GL_FLOAT,
-								 GL_FALSE,
-								 sizeof(PerVertexData),
-								 reinterpret_cast<void*>(0));
-
+	glEnableVertexAttribArray(attr_texture_position_);
+}
+
+void BlitProgram::draw_and_deactivate(const std::vector<Arguments>& arguments) {
+	size_t i = 0;
+
+	gl_array_buffer_.bind();
+
+	Gl::vertex_attrib_pointer(attr_blend_, 4, sizeof(PerVertexData), offsetof(PerVertexData, blend_r));
+	Gl::vertex_attrib_pointer(attr_mask_texture_position_,
+	                       2,
+	                       sizeof(PerVertexData),
+	                       offsetof(PerVertexData, mask_texture_x));
+	Gl::vertex_attrib_pointer(attr_position_, 3, sizeof(PerVertexData), offsetof(PerVertexData, gl_x));
+	Gl::vertex_attrib_pointer(
+	   attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
 
 	glUniform1i(u_texture_, 0);
-	glUniform1f(u_opacity_, opacity);
-	glUniform4f(u_dst_rect_, gl_dest_rect.x, gl_dest_rect.y, gl_dest_rect.w, gl_dest_rect.h);
-	glUniform4f(u_src_rect_, gl_src_rect.x, gl_src_rect.y, gl_src_rect.w, gl_src_rect.h);
-
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, gl_texture);
-
-	if (blend_mode == BlendMode::Copy) {
-		glBlendFunc(GL_ONE, GL_ZERO);
-	}
-}
-
-void BlitProgram::draw_and_deactivate(BlendMode blend_mode) {
-	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
-	if (blend_mode == BlendMode::Copy) {
-		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-	}
-
+	glUniform1i(u_mask_, 1);
+
+	// Prepare the buffer for many draw calls.
+	std::vector<DrawBatch> draw_batches;
+	int offset = 0;
+	vertices_.clear();
+	while (i < arguments.size()) {
+		const Arguments& template_args = arguments[i];
+
+		// Batch common blit operations up.
+		while (i < arguments.size()) {
+			const Arguments& current_args = arguments[i];
+			if (current_args.blend_mode != template_args.blend_mode ||
+					current_args.texture.name != template_args.texture.name ||
+					current_args.mask.name != template_args.mask.name) {
+				break;
+			}
+
+			const float blend_r = current_args.blend.r / 255.;
+			const float blend_g = current_args.blend.g / 255.;
+			const float blend_b = current_args.blend.b / 255.;
+			const float blend_a = current_args.blend.a / 255.;
+
+			vertices_.emplace_back(current_args.destination_rect.x,
+					current_args.destination_rect.y,
+					current_args.z_value,
+					current_args.texture.source_rect.x,
+					current_args.texture.source_rect.y,
+					current_args.mask.source_rect.x,
+					current_args.mask.source_rect.y,
+					blend_r,
+					blend_g,
+					blend_b,
+					blend_a);
+
+			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
+					current_args.destination_rect.y,
+					current_args.z_value,
+					current_args.texture.source_rect.x + current_args.texture.source_rect.w,
+					current_args.texture.source_rect.y,
+					current_args.mask.source_rect.x + current_args.mask.source_rect.w,
+					current_args.mask.source_rect.y,
+					blend_r,
+					blend_g,
+					blend_b,
+					blend_a);
+
+			vertices_.emplace_back(current_args.destination_rect.x,
+					current_args.destination_rect.y + current_args.destination_rect.h,
+					current_args.z_value,
+					current_args.texture.source_rect.x,
+					current_args.texture.source_rect.y + current_args.texture.source_rect.h,
+					current_args.mask.source_rect.x,
+					current_args.mask.source_rect.y + current_args.mask.source_rect.h,
+					blend_r,
+					blend_g,
+					blend_b,
+					blend_a);
+
+			vertices_.emplace_back(vertices_.at(vertices_.size() - 2));
+			vertices_.emplace_back(vertices_.at(vertices_.size() - 2));
+
+			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
+					current_args.destination_rect.y + current_args.destination_rect.h,
+					current_args.z_value,
+					current_args.texture.source_rect.x + current_args.texture.source_rect.w,
+					current_args.texture.source_rect.y + current_args.texture.source_rect.h,
+					current_args.mask.source_rect.x + current_args.mask.source_rect.w,
+					current_args.mask.source_rect.y + current_args.mask.source_rect.h,
+					blend_r,
+					blend_g,
+					blend_b,
+					blend_a);
+			++i;
+		}
+
+		draw_batches.emplace_back(DrawBatch{offset,
+		                                    static_cast<int>(vertices_.size() - offset),
+		                                    template_args.texture.name,
+		                                    template_args.mask.name,
+		                                    template_args.blend_mode});
+		offset = vertices_.size();
+	}
+	gl_array_buffer_.update(vertices_);
+
+	// Now do the draw calls.
+	for (const auto& draw_arg : draw_batches) {
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_2D, draw_arg.texture);
+
+		glActiveTexture(GL_TEXTURE1);
+		glBindTexture(GL_TEXTURE_2D, draw_arg.mask);
+
+		if (draw_arg.blend_mode == BlendMode::Copy) {
+			glBlendFunc(GL_ONE, GL_ZERO);
+		}
+		glDrawArrays(GL_TRIANGLES, draw_arg.offset, draw_arg.count);
+
+		if (draw_arg.blend_mode == BlendMode::Copy) {
+			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+		}
+	}
+
+	glDisableVertexAttribArray(attr_blend_);
+	glDisableVertexAttribArray(attr_mask_texture_position_);
 	glDisableVertexAttribArray(attr_position_);
+	glDisableVertexAttribArray(attr_texture_position_);
+
+	glBindTexture(GL_TEXTURE_2D, 0);
+
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(GL_TEXTURE_2D, 0);
+
 	glBindBuffer(GL_ARRAY_BUFFER, 0);
 }
 
@@ -227,15 +355,31 @@
 	blit_program_.reset(new BlitProgram(kVanillaBlitFragmentShader));
 }
 
-
 void VanillaBlitProgram::draw(const FloatRect& gl_dest_rect,
-                       const FloatRect& gl_src_rect,
-                       const GLuint gl_texture,
-							  const float opacity,
-                       const BlendMode blend_mode) {
-	blit_program_->activate(gl_dest_rect, gl_src_rect, gl_texture, opacity, blend_mode);
-	blit_program_->draw_and_deactivate(blend_mode);
-}
+                              const float z_value,
+										const BlitSource& texture,
+                              const float opacity,
+                              const BlendMode blend_mode) {
+	draw({Arguments{gl_dest_rect, z_value, texture, opacity, blend_mode}});
+}
+
+void VanillaBlitProgram::draw(const std::vector<Arguments>& arguments) {
+	std::vector<BlitProgram::Arguments> blit_arguments;
+	for (const Arguments arg : arguments) {
+		blit_arguments.emplace_back(BlitProgram::Arguments{
+		   arg.destination_rect,
+		   arg.z_value,
+		   arg.texture,
+		   BlitSource{FloatRect(), 0},
+		   RGBAColor(255, 255, 255, arg.opacity * 255),
+		   arg.blend_mode,
+		});
+	}
+
+	blit_program_->activate();
+	blit_program_->draw_and_deactivate(blit_arguments);
+}
+
 
 // static
 MonochromeBlitProgram& MonochromeBlitProgram::instance() {
@@ -248,19 +392,30 @@
 
 MonochromeBlitProgram::MonochromeBlitProgram() {
 	blit_program_.reset(new BlitProgram(kMonochromeBlitFragmentShader));
-
-	u_blend_ = glGetUniformLocation(blit_program_->program_object(), "u_blend");
-}
-
-void MonochromeBlitProgram::draw(const FloatRect& gl_dest_rect,
-                       const FloatRect& gl_src_rect,
-                       const GLuint gl_texture,
-							  const RGBAColor& blend) {
-	blit_program_->activate(gl_dest_rect, gl_src_rect, gl_texture, blend.a / 255., BlendMode::UseAlpha);
-
-	glUniform3f(u_blend_, blend.r / 255., blend.g / 255., blend.b / 255.);
-
-	blit_program_->draw_and_deactivate(BlendMode::UseAlpha);
+}
+
+void MonochromeBlitProgram::draw(const FloatRect& dest_rect,
+                                 const float z_value,
+											const BlitSource& texture,
+                                 const RGBAColor& blend) {
+	draw({Arguments{dest_rect, z_value, texture, blend, BlendMode::UseAlpha}});
+}
+
+void MonochromeBlitProgram::draw(const std::vector<Arguments>& arguments) {
+	std::vector<BlitProgram::Arguments> blit_arguments;
+	for (const Arguments arg : arguments) {
+		blit_arguments.emplace_back(BlitProgram::Arguments{
+		   arg.destination_rect,
+		   arg.z_value,
+		   arg.texture,
+		   BlitSource{FloatRect(), 0},
+		   arg.blend,
+		   arg.blend_mode,
+		});
+	}
+
+	blit_program_->activate();
+	blit_program_->draw_and_deactivate(blit_arguments);
 }
 
 // static
@@ -274,28 +429,29 @@
 
 BlendedBlitProgram::BlendedBlitProgram() {
 	blit_program_.reset(new BlitProgram(kBlendedBlitFragmentShader));
-	u_blend_ = glGetUniformLocation(blit_program_->program_object(), "u_blend");
-	u_mask_ = glGetUniformLocation(blit_program_->program_object(), "u_mask");
 }
 
 void BlendedBlitProgram::draw(const FloatRect& gl_dest_rect,
-                       const FloatRect& gl_src_rect,
-                       const GLuint gl_texture_image,
-							  const GLuint gl_texture_mask,
-							  const RGBAColor& blend) {
-	blit_program_->activate(gl_dest_rect, gl_src_rect, gl_texture_image, blend.a / 255., BlendMode::UseAlpha);
-
-	glActiveTexture(GL_TEXTURE1);
-	glBindTexture(GL_TEXTURE_2D, gl_texture_mask);
-	glUniform1i(u_mask_, 1);
-
-	glUniform3f(u_blend_, blend.r / 255., blend.g / 255., blend.b / 255.);
-
-	blit_program_->draw_and_deactivate(BlendMode::UseAlpha);
-
-	glActiveTexture(GL_TEXTURE1);
-	glBindTexture(GL_TEXTURE_2D, 0);
-
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, 0);
+                              const float z_value,
+										const BlitSource& texture,
+										const BlitSource& mask,
+                              const RGBAColor& blend) {
+	draw({Arguments{gl_dest_rect, z_value, texture, mask, blend, BlendMode::UseAlpha}});
+}
+
+void BlendedBlitProgram::draw(const std::vector<Arguments>& arguments) {
+	std::vector<BlitProgram::Arguments> blit_arguments;
+	for (const Arguments arg : arguments) {
+		blit_arguments.emplace_back(BlitProgram::Arguments{
+		   arg.destination_rect,
+		   arg.z_value,
+		   arg.texture,
+			arg.mask,
+		   arg.blend,
+		   arg.blend_mode,
+		});
+	}
+
+	blit_program_->activate();
+	blit_program_->draw_and_deactivate(blit_arguments);
 }

=== modified file 'src/graphic/gl/blit_program.h'
--- src/graphic/gl/blit_program.h	2014-12-14 14:38:37 +0000
+++ src/graphic/gl/blit_program.h	2015-02-21 07:13:48 +0000
@@ -21,32 +21,46 @@
 #define WL_GRAPHIC_GL_BLIT_PROGRAM_H
 
 #include <memory>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/rect.h"
 #include "graphic/blend_mode.h"
 #include "graphic/color.h"
+#include "graphic/gl/blit_source.h"
 #include "graphic/gl/system_headers.h"
 
 class BlitProgram;
 
+
 class VanillaBlitProgram {
 public:
+	struct Arguments {
+		FloatRect destination_rect;
+		float z_value;
+		BlitSource texture;
+		float opacity;
+		BlendMode blend_mode;
+	};
+
 	// Returns the (singleton) instance of this class.
 	static VanillaBlitProgram& instance();
 	~VanillaBlitProgram();
 
 	// Draws the rectangle 'gl_src_rect' from the texture with the name
-	// 'gl_texture' to 'gl_dest_rect' in the currently bound framebuffer. All alpha
+	// 'texture' to 'gl_dest_rect' in the currently bound framebuffer. All alpha
 	// values are multiplied by 'opacity' during the blit.
 	// All coordinates are in the OpenGL frame. The 'blend_mode' defines if the
 	// values are copied or if alpha values are used.
 	void draw(const FloatRect& gl_dest_rect,
-	          const FloatRect& gl_src_rect,
-	          const GLuint gl_texture,
+				 const float z_value,
+				 const BlitSource& texture,
 				 float opacity,
 	          const BlendMode blend_mode);
 
+	// Draws a bunch of items at once.
+	void draw(const std::vector<Arguments>& arguments);
+
 private:
 	VanillaBlitProgram();
 
@@ -57,55 +71,71 @@
 
 class MonochromeBlitProgram {
 public:
+	struct Arguments {
+		FloatRect destination_rect;
+		float z_value;
+		BlitSource texture;
+		RGBAColor blend;
+		BlendMode blend_mode;
+	};
+
 	// Returns the (singleton) instance of this class.
 	static MonochromeBlitProgram& instance();
 	~MonochromeBlitProgram();
 
 	// Draws the rectangle 'gl_src_rect' from the texture with the name
-	// 'gl_texture' to 'gl_dest_rect' in the currently bound framebuffer. All
+	// 'texture' to 'gl_dest_rect' in the currently bound framebuffer. All
 	// coordinates are in the OpenGL frame. The image is first converted to
 	// luminance, then all values are multiplied with blend.
 	void draw(const FloatRect& gl_dest_rect,
-	          const FloatRect& gl_src_rect,
-	          const GLuint gl_texture,
+				 const float z_value,
+				 const BlitSource& blit_source,
 				 const RGBAColor& blend);
 
+	// Draws a bunch of items at once.
+	void draw(const std::vector<Arguments>& arguments);
+
 private:
 	MonochromeBlitProgram();
 
 	std::unique_ptr<BlitProgram> blit_program_;
 
-	// Uniforms.
-	GLint u_blend_;
-
 	DISALLOW_COPY_AND_ASSIGN(MonochromeBlitProgram);
 };
 
 class BlendedBlitProgram {
 public:
+	struct Arguments {
+		FloatRect destination_rect;
+		float z_value;
+		BlitSource texture;
+		BlitSource mask;
+		RGBAColor blend;
+		BlendMode blend_mode;
+	};
+
 	// Returns the (singleton) instance of this class.
 	static BlendedBlitProgram& instance();
 	~BlendedBlitProgram();
 
 	// Draws the rectangle 'gl_src_rect' from the texture with the name
 	// 'gl_texture_image' to 'gl_dest_rect' in the currently bound framebuffer. All
-	// coordinates are in the OpenGL frame. The 'gl_texture_mask' is used to selectively apply
+	// coordinates are in the OpenGL frame. The 'texture_mask' is used to selectively apply
 	// the 'blend'. This is used for blitting player colored images.
 	void draw(const FloatRect& gl_dest_rect,
-	          const FloatRect& gl_src_rect,
-	          const GLuint gl_texture_image,
-	          const GLuint gl_texture_mask,
-				 const RGBAColor& blend);
+	          const float z_value,
+				 const BlitSource& texture,
+				 const BlitSource& mask,
+	          const RGBAColor& blend);
+
+	// Draws a bunch of items at once.
+	void draw(const std::vector<Arguments>& arguments);
 
 private:
 	BlendedBlitProgram();
 
 	std::unique_ptr<BlitProgram> blit_program_;
 
-	// Uniforms.
-	GLint u_blend_;
-	GLint u_mask_;
-
 	DISALLOW_COPY_AND_ASSIGN(BlendedBlitProgram);
 };
 

=== added file 'src/graphic/gl/blit_source.h'
--- src/graphic/gl/blit_source.h	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/blit_source.h	2015-02-21 07:13:48 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006-2015 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_GRAPHIC_GL_BLIT_SOURCE_H
+#define WL_GRAPHIC_GL_BLIT_SOURCE_H
+
+// The tuple of GL texture and rectangle in its locale coordinate frame that
+// defines the source of a blit operation.
+struct BlitSource {
+	FloatRect source_rect;
+	int name;
+};
+
+#endif  // end of include guard: WL_GRAPHIC_GL_BLIT_SOURCE_H

=== added file 'src/graphic/gl/coordinate_conversion.h'
--- src/graphic/gl/coordinate_conversion.h	1970-01-01 00:00:00 +0000
+++ src/graphic/gl/coordinate_conversion.h	2015-02-21 07:13:48 +0000
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2006-2015 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_GRAPHIC_GL_COORDINATE_CONVERSION_H
+#define WL_GRAPHIC_GL_COORDINATE_CONVERSION_H
+
+#include "base/rect.h"
+
+// Convert the 'rect' in pixel space into opengl space.
+enum class ConversionMode {
+	// Convert the rect as given.
+	kLeftBottom,
+
+	// Convert the rect so that the borders are in the center
+	// of the pixels.
+	kMidPoint,
+};
+
+// Converts the pixel (x, y) in a texture to a gl coordinate in [0, 1].
+inline void pixel_to_gl_texture(const int width, const int height, float* x, float* y) {
+	*x = (*x / width);
+	*y = 1. - (*y / height);
+}
+
+// Converts the given pixel into an OpenGl point in the renderbuffer.
+inline void pixel_to_gl_renderbuffer(const int width, const int height, float* x, float* y) {
+	*x = (*x / width) * 2. - 1.;
+	*y = 1. - (*y / height) * 2.;
+}
+
+// Converts 'rect' given on a screen of 'width' x 'height' pixels into a rect
+// in opengl coordinates in a renderbuffer, i.e. in [-1, 1]. The edges The returned
+// rectangle has positive width and height.
+inline FloatRect
+rect_to_gl_renderbuffer(const int width, const int height, const Rect& rect) {
+	float left = rect.x;
+	float top = rect.y;
+	float right = rect.x + rect.w;
+	float bottom = rect.y + rect.h;
+	pixel_to_gl_renderbuffer(width, height, &left, &top);
+	pixel_to_gl_renderbuffer(width, height, &right, &bottom);
+	return FloatRect(left, bottom, right - left, top - bottom);
+}
+
+// Converts 'rect' given on a texture of 'width' x 'height' pixels into a rect
+// in opengl coordinates in a texture, i.e. in [0, 1]. Texture pixels are sampled in their center.
+// The returned rectangle has positive width and height.
+inline FloatRect
+rect_to_gl_texture(const int width, const int height, const FloatRect& rect) {
+	float left = rect.x;
+	float top = rect.y;
+	float right = rect.x + rect.w;
+	float bottom = rect.y + rect.h;
+	pixel_to_gl_texture(width, height, &left, &top);
+	pixel_to_gl_texture(width, height, &right, &bottom);
+	return FloatRect(left, bottom, right - left, top - bottom);
+}
+
+#endif  // end of include guard: WL_GRAPHIC_GL_COORDINATE_CONVERSION_H

=== modified file 'src/graphic/gl/dither_program.cc'
--- src/graphic/gl/dither_program.cc	2014-12-04 10:55:47 +0000
+++ src/graphic/gl/dither_program.cc	2015-02-21 07:13:48 +0000
@@ -37,6 +37,8 @@
 attribute vec2 attr_texture_offset;
 attribute vec2 attr_texture_position;
 
+uniform float u_z_value;
+
 // Output of vertex shader.
 varying float var_brightness;
 varying vec2 var_dither_texture_position;
@@ -48,7 +50,7 @@
 	var_dither_texture_position = attr_dither_texture_position;
 	var_texture_offset = attr_texture_offset;
 	var_texture_position = attr_texture_position;
-	gl_Position = vec4(attr_position, 0., 1.);
+	gl_Position = vec4(attr_position, u_z_value, 1.);
 }
 )";
 
@@ -67,9 +69,8 @@
 void main() {
 	vec4 clr = texture2D(u_terrain_texture,
 			var_texture_offset + u_texture_dimensions * fract(var_texture_position));
-	clr.rgb *= var_brightness;
-	clr.a = 1. - texture2D(u_dither_texture, var_dither_texture_position).a;
-	gl_FragColor = clr;
+	gl_FragColor = vec4(clr.rgb * var_brightness,
+			1. - texture2D(u_dither_texture, var_dither_texture_position).a);
 }
 )";
 
@@ -87,12 +88,13 @@
 	u_dither_texture_ = glGetUniformLocation(gl_program_.object(), "u_dither_texture");
 	u_terrain_texture_ = glGetUniformLocation(gl_program_.object(), "u_terrain_texture");
 	u_texture_dimensions_ = glGetUniformLocation(gl_program_.object(), "u_texture_dimensions");
+	u_z_value_ = glGetUniformLocation(gl_program_.object(), "u_z_value");
 
 	dither_mask_.reset(new Texture(load_image_as_sdl_surface("world/pics/edge.png", g_fs), true));
 
 	glBindTexture(GL_TEXTURE_2D, dither_mask_->get_gl_texture());
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(GL_CLAMP));
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(GL_CLAMP));
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(GL_CLAMP_TO_EDGE));
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(GL_CLAMP_TO_EDGE));
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(GL_LINEAR));
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(GL_LINEAR));
 	glBindTexture(GL_TEXTURE_2D, 0);
@@ -116,16 +118,16 @@
 
 	switch (order_index) {
 	case 0:
+		back.dither_texture_x = 1.;
+		back.dither_texture_y = 1.;
+		break;
+	case 1:
 		back.dither_texture_x = 0.;
-		back.dither_texture_y = 0.;
-		break;
-	case 1:
-		back.dither_texture_x = 1.;
-		back.dither_texture_y = 0.;
+		back.dither_texture_y = 1.;
 		break;
 	case 2:
 		back.dither_texture_x = 0.5;
-		back.dither_texture_y = 1.;
+		back.dither_texture_y = 0.;
 		break;
 	default:
 		throw wexception("Never here.");
@@ -149,14 +151,14 @@
 	if (terrains.get_unmutable(my_terrain).dither_layer() <
 	    other_terrain_description.dither_layer()) {
 		const FloatPoint texture_offset =
-		   other_terrain_description.get_texture(gametime).texture_coordinates().top_left();
+		   other_terrain_description.get_texture(gametime).texture_coordinates().origin();
 		add_vertex(fields_to_draw.at(idx1), 0, texture_offset);
 		add_vertex(fields_to_draw.at(idx2), 1, texture_offset);
 		add_vertex(fields_to_draw.at(idx3), 2, texture_offset);
 	}
 }
 
-void DitherProgram::gl_draw(int gl_texture, float texture_w, float texture_h) {
+void DitherProgram::gl_draw(int gl_texture, float texture_w, float texture_h, const float z_value) {
 	glUseProgram(gl_program_.object());
 
 	glEnableVertexAttribArray(attr_brightness_);
@@ -165,25 +167,20 @@
 	glEnableVertexAttribArray(attr_texture_offset_);
 	glEnableVertexAttribArray(attr_texture_position_);
 
-	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
-	glBufferData(GL_ARRAY_BUFFER,
-	             sizeof(PerVertexData) * vertices_.size(),
-	             vertices_.data(),
-	             GL_STREAM_DRAW);
+	gl_array_buffer_.bind();
+	gl_array_buffer_.update(vertices_);
 
-	const auto set_attrib_pointer = [](const int vertex_index, int num_items, int offset) {
-		glVertexAttribPointer(vertex_index,
-		                      num_items,
-		                      GL_FLOAT,
-		                      GL_FALSE,
-		                      sizeof(PerVertexData),
-		                      reinterpret_cast<void*>(offset));
-	};
-	set_attrib_pointer(attr_brightness_, 1, offsetof(PerVertexData, brightness));
-	set_attrib_pointer(attr_dither_texture_position_, 2, offsetof(PerVertexData, dither_texture_x));
-	set_attrib_pointer(attr_position_, 2, offsetof(PerVertexData, gl_x));
-	set_attrib_pointer(attr_texture_offset_, 2, offsetof(PerVertexData, texture_offset_x));
-	set_attrib_pointer(attr_texture_position_, 2, offsetof(PerVertexData, texture_x));
+	Gl::vertex_attrib_pointer(
+	   attr_brightness_, 1, sizeof(PerVertexData), offsetof(PerVertexData, brightness));
+	Gl::vertex_attrib_pointer(attr_dither_texture_position_,
+	                       2,
+	                       sizeof(PerVertexData),
+	                       offsetof(PerVertexData, dither_texture_x));
+	Gl::vertex_attrib_pointer(attr_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, gl_x));
+	Gl::vertex_attrib_pointer(
+	   attr_texture_offset_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_offset_x));
+	Gl::vertex_attrib_pointer(
+	   attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
 
 	glBindBuffer(GL_ARRAY_BUFFER, 0);
 
@@ -194,6 +191,7 @@
 	glActiveTexture(GL_TEXTURE1);
 	glBindTexture(GL_TEXTURE_2D, gl_texture);
 
+	glUniform1f(u_z_value_, z_value);
 	glUniform1i(u_dither_texture_, 0);
 	glUniform1i(u_terrain_texture_, 1);
 	glUniform2f(u_texture_dimensions_, texture_w, texture_h);
@@ -213,7 +211,8 @@
 
 void DitherProgram::draw(const uint32_t gametime,
                          const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
-                         const FieldsToDraw& fields_to_draw) {
+                         const FieldsToDraw& fields_to_draw,
+                         const float z_value) {
 	// This method expects that all terrains have the same dimensions and that
 	// all are packed into the same texture atlas, i.e. all are in the same GL
 	// texture. It does not check for this invariance for speeds sake.
@@ -271,5 +270,8 @@
 	}
 
 	const Texture& texture = terrains.get_unmutable(0).get_texture(0);
-	gl_draw(texture.get_gl_texture(), texture.texture_coordinates().w, texture.texture_coordinates().h);
+	gl_draw(texture.get_gl_texture(),
+	        texture.texture_coordinates().w,
+	        texture.texture_coordinates().h,
+	        z_value);
 }

=== modified file 'src/graphic/gl/dither_program.h'
--- src/graphic/gl/dither_program.h	2014-11-27 18:45:44 +0000
+++ src/graphic/gl/dither_program.h	2015-02-21 07:13:48 +0000
@@ -38,7 +38,8 @@
 	// Draws the terrain.
 	void draw(uint32_t gametime,
 	          const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
-	          const FieldsToDraw& fields_to_draw);
+	          const FieldsToDraw& fields_to_draw,
+	          float z_value);
 
 private:
 	// Adds the triangle between the indexes (which index 'fields_to_draw') to
@@ -72,13 +73,13 @@
 	};
 
 	// Call through to GL.
-	void gl_draw(int gl_texture, float texture_w, float texture_h);
+	void gl_draw(int gl_texture, float texture_w, float texture_h, float z_value);
 
 	// The program used for drawing the terrain.
 	Gl::Program gl_program_;
 
 	// The buffer that contains the data to be rendered.
-	Gl::Buffer gl_array_buffer_;
+	Gl::NewBuffer<PerVertexData> gl_array_buffer_;
 
 	// Attributes.
 	GLint attr_brightness_;
@@ -91,6 +92,7 @@
 	GLint u_dither_texture_;
 	GLint u_terrain_texture_;
 	GLint u_texture_dimensions_;
+	GLint u_z_value_;
 
 	// The texture mask for the dithering step.
 	std::unique_ptr<Texture> dither_mask_;

=== modified file 'src/graphic/gl/draw_line_program.cc'
--- src/graphic/gl/draw_line_program.cc	2014-11-08 14:59:03 +0000
+++ src/graphic/gl/draw_line_program.cc	2015-02-21 07:13:48 +0000
@@ -19,6 +19,8 @@
 
 #include "graphic/gl/draw_line_program.h"
 
+#include <algorithm>
+#include <cassert>
 #include <vector>
 
 #include "base/log.h"
@@ -29,23 +31,33 @@
 #version 120
 
 // Attributes.
-attribute vec2 attr_position;
+attribute vec3 attr_position;
+attribute vec3 attr_color;
+
+varying vec3 var_color;
 
 void main() {
-	gl_Position = vec4(attr_position, 0., 1.);
+	var_color = attr_color;
+	gl_Position = vec4(attr_position, 1.);
 }
 )";
 
 const char kDrawLineFragmentShader[] = R"(
 #version 120
 
-uniform ivec3 u_color;
+varying vec3 var_color;
 
 void main() {
-	gl_FragColor = vec4(vec3(u_color) / 255., 1.);
+	gl_FragColor = vec4(var_color.rgb, 1.);
 }
 )";
 
+struct DrawBatch {
+	int offset;
+	int count;
+	int line_width;
+};
+
 }  // namespace
 
 // static
@@ -58,37 +70,81 @@
 	gl_program_.build(kDrawLineVertexShader, kDrawLineFragmentShader);
 
 	attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");
-	u_color_ = glGetUniformLocation(gl_program_.object(), "u_color");
-
+	attr_color_ = glGetAttribLocation(gl_program_.object(), "attr_color");
 }
 
-void DrawLineProgram::draw(const float x1,
-                           const float y1,
-                           const float x2,
-                           const float y2,
+void DrawLineProgram::draw(const FloatPoint& start,
+                           const FloatPoint& end,
+                           const float z_value,
                            const RGBColor& color,
-                           const int line_width) {
+									int line_width) {
+	draw({Arguments{FloatRect(start.x, start.y, end.x - start.x, end.y - start.y),
+	                z_value,
+	                color,
+						 static_cast<uint8_t>(line_width),
+	                BlendMode::Copy}});
+}
+
+void DrawLineProgram::draw(std::vector<Arguments> arguments) {
+	size_t i = 0;
+
 	glUseProgram(gl_program_.object());
 	glEnableVertexAttribArray(attr_position_);
-
-	const std::vector<PerVertexData> vertices = {{x1, y1}, {x2, y2}};
-
-	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
-	glBufferData(
-	   GL_ARRAY_BUFFER, sizeof(PerVertexData) * vertices.size(), vertices.data(), GL_STREAM_DRAW);
-	glVertexAttribPointer(attr_position_,
-								 2,
-								 GL_FLOAT,
-								 GL_FALSE,
-								 sizeof(PerVertexData),
-								 reinterpret_cast<void*>(0));
-
-	glUniform3i(u_color_, color.r, color.g, color.b);
-
-	glLineWidth(line_width);
-	glDrawArrays(GL_LINES, 0, 2);
+	glEnableVertexAttribArray(attr_color_);
+
+	gl_array_buffer_.bind();
+
+	Gl::vertex_attrib_pointer(attr_position_, 3, sizeof(PerVertexData), offsetof(PerVertexData, gl_x));
+	Gl::vertex_attrib_pointer(attr_color_, 3, sizeof(PerVertexData), offsetof(PerVertexData, color_r));
+
+	vertices_.clear();
+
+	std::vector<DrawBatch> draw_batches;
+	int offset = 0;
+	while (i < arguments.size()) {
+		const Arguments& template_args = arguments[i];
+
+		while (i < arguments.size()) {
+			const Arguments& current_args = arguments[i];
+			if (current_args.line_width != template_args.line_width) {
+				break;
+			}
+			// We do not support anything else for drawing lines, really.
+			assert(current_args.blend_mode == BlendMode::Copy);
+
+			vertices_.emplace_back(current_args.destination_rect.x,
+			                       current_args.destination_rect.y,
+			                       current_args.z_value,
+			                       current_args.color.r / 255.,
+			                       current_args.color.g / 255.,
+			                       current_args.color.b / 255.);
+
+			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
+			                       current_args.destination_rect.y + current_args.destination_rect.h,
+			                       current_args.z_value,
+			                       current_args.color.r / 255.,
+			                       current_args.color.g / 255.,
+			                       current_args.color.b / 255.);
+			++i;
+		}
+
+		draw_batches.emplace_back(
+		   DrawBatch{offset, static_cast<int>(vertices_.size() - offset), template_args.line_width});
+		offset = vertices_.size();
+	}
+
+	gl_array_buffer_.update(vertices_);
+
+	// Now do the draw calls.
+	for (const auto& draw_arg : draw_batches) {
+		glLineWidth(draw_arg.line_width);
+		glDrawArrays(GL_LINES, draw_arg.offset, draw_arg.count);
+	}
 
 	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
 	glDisableVertexAttribArray(attr_position_);
+	glDisableVertexAttribArray(attr_color_);
+
 	glUseProgram(0);
 }

=== modified file 'src/graphic/gl/draw_line_program.h'
--- src/graphic/gl/draw_line_program.h	2014-11-08 18:06:17 +0000
+++ src/graphic/gl/draw_line_program.h	2015-02-21 07:13:48 +0000
@@ -20,37 +20,76 @@
 #ifndef WL_GRAPHIC_GL_DRAW_LINE_PROGRAM_H
 #define WL_GRAPHIC_GL_DRAW_LINE_PROGRAM_H
 
+#include <vector>
+
+#include "base/point.h"
+#include "base/rect.h"
+#include "graphic/blend_mode.h"
 #include "graphic/color.h"
 #include "graphic/gl/utils.h"
 
 class DrawLineProgram {
 public:
+	struct Arguments {
+		// The line is drawn from the top left to the bottom right of
+		// this rectangle.
+		FloatRect destination_rect;
+		float z_value;
+		RGBAColor color;
+		uint8_t line_width;
+		BlendMode blend_mode;
+	};
+
 	// Returns the (singleton) instance of this class.
 	static DrawLineProgram& instance();
 
 	// Draws a line from (x1, y1) to (x2, y2) which are in gl
 	// coordinates in 'color' with a 'line_width' in pixels.
-	void draw(float x1, float y1, float x2, float y2, const RGBColor& color, int line_width);
+	void draw(const FloatPoint& start,
+	          const FloatPoint& end,
+	          const float z_value,
+	          const RGBColor& color,
+	          const int line_width);
+
+	void draw(std::vector<Arguments> arguments);
+
 
 private:
 	DrawLineProgram();
 
 	struct PerVertexData {
-		float gl_x, gl_y;
+		PerVertexData(float init_gl_x,
+		              float init_gl_y,
+		              float init_gl_z,
+		              float init_color_r,
+		              float init_color_g,
+		              float init_color_b)
+		   : gl_x(init_gl_x),
+		     gl_y(init_gl_y),
+		     gl_z(init_gl_z),
+		     color_r(init_color_r),
+		     color_g(init_color_g),
+		     color_b(init_color_b) {
+		}
+
+		float gl_x, gl_y, gl_z;
+		float color_r, color_g, color_b;
 	};
-	static_assert(sizeof(PerVertexData) == 8, "Wrong padding.");
+	static_assert(sizeof(PerVertexData) == 24, "Wrong padding.");
+
+	// This is only kept around so that we do not constantly
+	// allocate memory for it.
+	std::vector<PerVertexData> vertices_;
 
 	// The buffer that contains the vertices for rendering.
-	Gl::Buffer gl_array_buffer_;
+	Gl::NewBuffer<PerVertexData> gl_array_buffer_;
 
 	// The program.
 	Gl::Program gl_program_;
 
 	// Attributes.
 	GLint attr_position_;
-
-	// Uniforms.
-	GLint u_color_;
+	GLint attr_color_;
 
 	DISALLOW_COPY_AND_ASSIGN(DrawLineProgram);
 };

=== modified file 'src/graphic/gl/fields_to_draw.h'
--- src/graphic/gl/fields_to_draw.h	2015-02-08 18:16:41 +0000
+++ src/graphic/gl/fields_to_draw.h	2015-02-21 07:13:48 +0000
@@ -43,14 +43,19 @@
 		const RoadTextures* road_textures; // Road Textures to use for drawing.
 	};
 
-	FieldsToDraw(int minfx, int maxfx, int minfy, int maxfy)
-	   : min_fx_(minfx),
-	     max_fx_(maxfx),
-	     min_fy_(minfy),
-	     max_fy_(maxfy),
-	     w_(max_fx_ - min_fx_ + 1),
-	     h_(max_fy_ - min_fy_ + 1) {
-		fields_.resize(w_ * h_);
+	FieldsToDraw() = default;
+
+	// Resize this fields to draw for reuse.
+	void reset(int minfx, int maxfx, int minfy, int maxfy) {
+		min_fx_ = minfx;
+		max_fx_ = maxfx;
+		min_fy_ = minfy;
+		max_fy_ = maxfy;
+		w_ = max_fx_ - min_fx_ + 1;
+		h_ = max_fy_ - min_fy_ + 1;
+		if (fields_.size() < static_cast<size_t>(w_ * h_)) {
+			fields_.resize(w_ * h_);
+		}
 	}
 
 	// Calculates the index of the given field with ('fx', 'fy') being geometric
@@ -84,14 +89,14 @@
 
 private:
 	// Minimum and maximum field coordinates (geometric) to render. Can be negative.
-	const int min_fx_;
-	const int max_fx_;
-	const int min_fy_;
-	const int max_fy_;
+	int min_fx_;
+	int max_fx_;
+	int min_fy_;
+	int max_fy_;
 
 	// Width and height in number of fields.
-	const int w_;
-	const int h_;
+	int w_;
+	int h_;
 
 	std::vector<Field> fields_;
 };

=== modified file 'src/graphic/gl/fill_rect_program.cc'
--- src/graphic/gl/fill_rect_program.cc	2014-11-02 16:55:53 +0000
+++ src/graphic/gl/fill_rect_program.cc	2015-02-21 07:13:48 +0000
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include "base/log.h"
+#include "base/wexception.h"
 
 namespace  {
 
@@ -29,26 +30,24 @@
 #version 120
 
 // Attributes.
-attribute vec2 attr_position;
-
-// Uniforms.
-uniform vec4 u_rect;
-
+attribute vec3 attr_position;
+attribute vec4 attr_color;
+
+varying vec4 var_color;
 
 void main() {
-	float x = u_rect.x + attr_position.x * u_rect.z;
-	float y = u_rect.y + attr_position.y * u_rect.w;
-	gl_Position = vec4(x, y, 0., 1.);
+	var_color = attr_color;
+	gl_Position = vec4(attr_position, 1.);
 }
 )";
 
 const char kFillRectFragmentShader[] = R"(
 #version 120
 
-uniform ivec4 u_color;
+varying vec4 var_color;
 
 void main() {
-	gl_FragColor = vec4(u_color) / 255.;
+	gl_FragColor = var_color;
 }
 )";
 
@@ -64,49 +63,144 @@
 	gl_program_.build(kFillRectVertexShader, kFillRectFragmentShader);
 
 	attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");
-
-	u_color_ = glGetUniformLocation(gl_program_.object(), "u_color");
-	u_rect_ = glGetUniformLocation(gl_program_.object(), "u_rect");
-
-	std::vector<PerVertexData> vertices;
-	vertices.push_back(PerVertexData
-			{0., 1.});
-	vertices.push_back(PerVertexData
-			{1., 1.});
-	vertices.push_back(PerVertexData
-			{0., 0.});
-	vertices.push_back(PerVertexData
-			{1., 0.});
-
-	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
-	glBufferData(
-	   GL_ARRAY_BUFFER, sizeof(PerVertexData) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
-	glVertexAttribPointer(attr_position_,
-								 2,
-								 GL_FLOAT,
-								 GL_FALSE,
-								 sizeof(PerVertexData),
-								 reinterpret_cast<void*>(0));
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
-}
-
-void FillRectProgram::draw(const FloatRect& gl_dst_rect, const RGBAColor& color) {
-	glUseProgram(gl_program_.object());
-	glEnableVertexAttribArray(attr_position_);
-	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
-
-	glVertexAttribPointer(attr_position_,
-								 2,
-								 GL_FLOAT,
-								 GL_FALSE,
-								 sizeof(PerVertexData),
-								 reinterpret_cast<void*>(0));
-
-	glUniform4f(u_rect_, gl_dst_rect.x, gl_dst_rect.y, gl_dst_rect.w, gl_dst_rect.h);
-	glUniform4i(u_color_, color.r, color.g, color.b, color.a);
-
-	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+	attr_color_ = glGetAttribLocation(gl_program_.object(), "attr_color");
+}
+
+void FillRectProgram::draw(const FloatRect& destination_rect,
+                           const float z_value,
+                           const RGBAColor& color,
+                           const BlendMode blend_mode) {
+	draw({Arguments{destination_rect, z_value, color, blend_mode} });
+}
+
+void FillRectProgram::draw(const std::vector<Arguments>& arguments) {
+	size_t i = 0;
+
+	while (i < arguments.size()) {
+		vertices_.clear();
+		const Arguments& template_args = arguments[i];
+
+		// This method does 3 things:
+		// - if blend_mode is Copy, we will copy color into the destination
+		// pixels without blending.
+		// - if blend_mode is Alpha and color.r < 0, we will
+		// GL_FUNC_REVERSE_SUBTRACT color.r from all RGB values in the
+		// destination buffer. color.a should be 0 for this.
+		// - if blend_mode is Alpha and color.r > 0, we will
+		// GL_ADD color.r to all RGB values in the destination buffer.
+		// color.a should be 0 for this.
+
+		// The simple trick here is to fill the rect, but using a different glBlendFunc that will sum
+		// src and target (or subtract them if factor is negative).
+		switch (template_args.blend_mode) {
+		case BlendMode::Subtract:
+			glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
+		/* fallthrough intended */
+		case BlendMode::UseAlpha:
+			glBlendFunc(GL_ONE, GL_ONE);
+			break;
+
+		case BlendMode::Copy:
+			glDisable(GL_BLEND);
+			break;
+
+		default:
+			break;
+		}
+
+		glUseProgram(gl_program_.object());
+
+		gl_array_buffer_.bind();
+
+		glEnableVertexAttribArray(attr_position_);
+		glEnableVertexAttribArray(attr_color_);
+
+		// Batch common rectangles up.
+		while (i < arguments.size()) {
+			const Arguments& current_args = arguments[i];
+			if (current_args.blend_mode != template_args.blend_mode) {
+				break;
+			}
+
+			const float r = current_args.color.r / 255.;
+			const float g = current_args.color.g / 255.;
+			const float b = current_args.color.b / 255.;
+			const float a = current_args.color.a / 255.;
+
+			// First triangle.
+			vertices_.emplace_back(current_args.destination_rect.x,
+			                      current_args.destination_rect.y,
+			                      current_args.z_value,
+			                      r,
+			                      g,
+			                      b,
+			                      a);
+			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
+			                      current_args.destination_rect.y,
+			                      current_args.z_value,
+			                      r,
+			                      g,
+			                      b,
+			                      a);
+			vertices_.emplace_back(current_args.destination_rect.x,
+			                      current_args.destination_rect.y + current_args.destination_rect.h,
+			                      current_args.z_value,
+			                      r,
+			                      g,
+			                      b,
+			                      a);
+
+			// Second triangle.
+			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
+			                      current_args.destination_rect.y,
+			                      current_args.z_value,
+			                      r,
+			                      g,
+			                      b,
+			                      a);
+			vertices_.emplace_back(current_args.destination_rect.x,
+			                      current_args.destination_rect.y + current_args.destination_rect.h,
+			                      current_args.z_value,
+			                      r,
+			                      g,
+			                      b,
+			                      a);
+			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
+			                      current_args.destination_rect.y + current_args.destination_rect.h,
+			                      current_args.z_value,
+			                      r,
+			                      g,
+			                      b,
+			                      a);
+			++i;
+		}
+
+		gl_array_buffer_.update(vertices_);
+
+		Gl::vertex_attrib_pointer(
+		   attr_position_, 3, sizeof(PerVertexData), offsetof(PerVertexData, gl_x));
+		Gl::vertex_attrib_pointer(attr_color_, 4, sizeof(PerVertexData), offsetof(PerVertexData, r));
+
+		glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
+
+		switch (template_args.blend_mode) {
+		case BlendMode::Subtract:
+			glBlendEquation(GL_FUNC_ADD);
+		/* fallthrough intended */
+		case BlendMode::UseAlpha:
+			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+			break;
+
+		case BlendMode::Copy:
+			glEnable(GL_BLEND);
+			break;
+
+		default:
+			break;
+		}
+	}
 
 	glDisableVertexAttribArray(attr_position_);
+	glDisableVertexAttribArray(attr_color_);
 	glBindBuffer(GL_ARRAY_BUFFER, 0);
 }

=== modified file 'src/graphic/gl/fill_rect_program.h'
--- src/graphic/gl/fill_rect_program.h	2014-11-08 18:06:17 +0000
+++ src/graphic/gl/fill_rect_program.h	2015-02-21 07:13:48 +0000
@@ -20,39 +20,73 @@
 #ifndef WL_GRAPHIC_GL_FILL_RECT_PROGRAM_H
 #define WL_GRAPHIC_GL_FILL_RECT_PROGRAM_H
 
+#include <vector>
+
 #include "base/rect.h"
+#include "graphic/blend_mode.h"
 #include "graphic/color.h"
 #include "graphic/gl/utils.h"
 
 class FillRectProgram {
 public:
+	struct Arguments {
+		FloatRect destination_rect;
+		float z_value;
+		RGBAColor color;
+		BlendMode blend_mode;
+	};
+
 	// Returns the (singleton) instance of this class.
 	static FillRectProgram& instance();
 
-	// Fills a solid rect in 'color' into the currently activated
-	// framebuffer.
-	void draw(const FloatRect& gl_dst_rect, const RGBAColor& color);
+	// Fills a solid rect in 'color'. If blend_mode is BlendMode::UseAlpha, this
+	// will brighten the rect, if it is BlendMode::Subtract it darkens it.
+	void draw(const FloatRect& destination_rect,
+	          float z_value,
+	          const RGBAColor& color,
+	          BlendMode blend_mode);
+
+
+	void draw(const std::vector<Arguments>& arguments);
 
 private:
 	FillRectProgram();
 
 	struct PerVertexData {
-		float gl_x, gl_y;
+		PerVertexData(float init_gl_x,
+		              float init_gl_y,
+		              float init_gl_z,
+		              float init_r,
+		              float init_g,
+		              float init_b,
+		              float init_a)
+		   : gl_x(init_gl_x),
+		     gl_y(init_gl_y),
+		     gl_z(init_gl_z),
+		     r(init_r),
+		     g(init_g),
+		     b(init_b),
+		     a(init_a) {
+		}
+
+		float gl_x, gl_y, gl_z;
+		float r, g, b, a;
 	};
-	static_assert(sizeof(PerVertexData) == 8, "Wrong padding.");
+	static_assert(sizeof(PerVertexData) == 28, "Wrong padding.");
+
+	// This is only kept around so that we do not constantly allocate memory for
+	// it.
+	std::vector<PerVertexData> vertices_;
 
 	// The buffer that will contain the quad for rendering.
-	Gl::Buffer gl_array_buffer_;
+	Gl::NewBuffer<PerVertexData> gl_array_buffer_;
 
 	// The program.
 	Gl::Program gl_program_;
 
 	// Attributes.
 	GLint attr_position_;
-
-	// Uniforms.
-	GLint u_rect_;
-	GLint u_color_;
+	GLint attr_color_;
 
 	DISALLOW_COPY_AND_ASSIGN(FillRectProgram);
 };

=== modified file 'src/graphic/gl/road_program.cc'
--- src/graphic/gl/road_program.cc	2015-02-08 18:16:41 +0000
+++ src/graphic/gl/road_program.cc	2015-02-21 07:13:48 +0000
@@ -23,6 +23,7 @@
 #include <cmath>
 
 #include "base/log.h"
+#include "graphic/gl/coordinate_conversion.h"
 #include "graphic/gl/fields_to_draw.h"
 #include "graphic/graphic.h"
 #include "graphic/image_io.h"
@@ -40,6 +41,8 @@
 attribute vec2 attr_texture_position;
 attribute float attr_brightness;
 
+uniform float u_z_value;
+
 // Outputs.
 varying vec2 out_texture_position;
 varying float out_brightness;
@@ -47,7 +50,7 @@
 void main() {
 	out_texture_position = attr_texture_position;
 	out_brightness = attr_brightness;
-	gl_Position = vec4(attr_position, 0., 1.);
+	gl_Position = vec4(attr_position, u_z_value, 1.);
 }
 )";
 
@@ -73,17 +76,18 @@
 	gl_program_.build(kRoadVertexShader, kRoadFragmentShader);
 
 	attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");
-	attr_texture_position_ =
-		glGetAttribLocation(gl_program_.object(), "attr_texture_position");
+	attr_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_texture_position");
 	attr_brightness_ = glGetAttribLocation(gl_program_.object(), "attr_brightness");
 
+	u_z_value_ = glGetUniformLocation(gl_program_.object(), "u_z_value");
 	u_texture_ = glGetUniformLocation(gl_program_.object(), "u_texture");
 }
 
 RoadProgram::~RoadProgram() {
 }
 
-void RoadProgram::add_road(const Surface& surface,
+void RoadProgram::add_road(const int renderbuffer_width,
+                           const int renderbuffer_height,
                            const FieldsToDraw::Field& start,
                            const FieldsToDraw::Field& end,
                            const Widelands::RoadType road_type,
@@ -127,16 +131,18 @@
 	   texture_rect.y,
 	   start.brightness,
 	});
-	surface.pixel_to_gl(&vertices_.back().gl_x, &vertices_.back().gl_y);
+	pixel_to_gl_renderbuffer(
+	   renderbuffer_width, renderbuffer_height, &vertices_.back().gl_x, &vertices_.back().gl_y);
 
 	vertices_.emplace_back(PerVertexData{
 	   start.pixel_x - road_overshoot_x - road_thickness_x,
 	   start.pixel_y - road_overshoot_y - road_thickness_y,
 	   texture_rect.x,
-		texture_rect.y + texture_rect.h,
+	   texture_rect.y + texture_rect.h,
 	   start.brightness,
 	});
-	surface.pixel_to_gl(&vertices_.back().gl_x, &vertices_.back().gl_y);
+	pixel_to_gl_renderbuffer(
+	   renderbuffer_width, renderbuffer_height, &vertices_.back().gl_x, &vertices_.back().gl_y);
 
 	vertices_.emplace_back(PerVertexData{
 	   end.pixel_x + road_overshoot_x + road_thickness_x,
@@ -145,7 +151,8 @@
 	   texture_rect.y,
 	   end.brightness,
 	});
-	surface.pixel_to_gl(&vertices_.back().gl_x, &vertices_.back().gl_y);
+	pixel_to_gl_renderbuffer(
+	   renderbuffer_width, renderbuffer_height, &vertices_.back().gl_x, &vertices_.back().gl_y);
 
 	// As OpenGl does not support drawing quads in modern days and we have a
 	// bunch of roads that might not be neighbored, we need to add two triangles
@@ -161,10 +168,14 @@
 	   texture_rect.y + texture_rect.h,
 	   end.brightness,
 	});
-	surface.pixel_to_gl(&vertices_.back().gl_x, &vertices_.back().gl_y);
+	pixel_to_gl_renderbuffer(
+	   renderbuffer_width, renderbuffer_height, &vertices_.back().gl_x, &vertices_.back().gl_y);
 }
 
-void RoadProgram::draw(const Surface& surface, const FieldsToDraw& fields_to_draw) {
+void RoadProgram::draw(const int renderbuffer_width,
+                       const int renderbuffer_height,
+                       const FieldsToDraw& fields_to_draw,
+                       float z_value) {
 	vertices_.clear();
 
 	int gl_texture = -1;
@@ -175,9 +186,15 @@
 		const int rn_index = fields_to_draw.calculate_index(field.fx + 1, field.fy);
 		if (rn_index != -1) {
 			const Widelands::RoadType road =
-				static_cast<Widelands::RoadType>(field.roads & Widelands::RoadType::kMask);
+			   static_cast<Widelands::RoadType>(field.roads & Widelands::RoadType::kMask);
 			if (road != Widelands::RoadType::kNone) {
-				add_road(surface, field, fields_to_draw.at(rn_index), road, kEast, &gl_texture);
+				add_road(renderbuffer_width,
+				         renderbuffer_height,
+				         field,
+				         fields_to_draw.at(rn_index),
+				         road,
+				         kEast,
+				         &gl_texture);
 			}
 		}
 
@@ -185,9 +202,15 @@
 		const int brn_index = fields_to_draw.calculate_index(field.fx + (field.fy & 1), field.fy + 1);
 		if (brn_index != -1) {
 			const Widelands::RoadType road =
-				static_cast<Widelands::RoadType>((field.roads >> 2) & Widelands::RoadType::kMask);
+			   static_cast<Widelands::RoadType>((field.roads >> 2) & Widelands::RoadType::kMask);
 			if (road != Widelands::RoadType::kNone) {
-				add_road(surface, field, fields_to_draw.at(brn_index), road, kSouthEast, &gl_texture);
+				add_road(renderbuffer_width,
+				         renderbuffer_height,
+				         field,
+				         fields_to_draw.at(brn_index),
+				         road,
+				         kSouthEast,
+				         &gl_texture);
 			}
 		}
 
@@ -196,9 +219,15 @@
 		   fields_to_draw.calculate_index(field.fx + (field.fy & 1) - 1, field.fy + 1);
 		if (bln_index != -1) {
 			const Widelands::RoadType road =
-				static_cast<Widelands::RoadType>((field.roads >> 4) & Widelands::RoadType::kMask);
+			   static_cast<Widelands::RoadType>((field.roads >> 4) & Widelands::RoadType::kMask);
 			if (road != Widelands::RoadType::kNone) {
-				add_road(surface, field, fields_to_draw.at(bln_index), road, kSouthWest, &gl_texture);
+				add_road(renderbuffer_width,
+				         renderbuffer_height,
+				         field,
+				         fields_to_draw.at(bln_index),
+				         road,
+				         kSouthWest,
+				         &gl_texture);
 			}
 		}
 	}
@@ -209,21 +238,15 @@
 	glEnableVertexAttribArray(attr_texture_position_);
 	glEnableVertexAttribArray(attr_brightness_);
 
-	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
-	glBufferData(
-	   GL_ARRAY_BUFFER, sizeof(PerVertexData) * vertices_.size(), vertices_.data(), GL_STREAM_DRAW);
+	gl_array_buffer_.bind();
+	gl_array_buffer_.update(vertices_);
 
-	const auto set_attrib_pointer = [](const int vertex_index, int num_items, int offset) {
-		glVertexAttribPointer(vertex_index,
-		                      num_items,
-		                      GL_FLOAT,
-		                      GL_FALSE,
-		                      sizeof(PerVertexData),
-		                      reinterpret_cast<void*>(offset));
-	};
-	set_attrib_pointer(attr_position_, 2, offsetof(PerVertexData, gl_x));
-	set_attrib_pointer(attr_texture_position_, 2, offsetof(PerVertexData, texture_x));
-	set_attrib_pointer(attr_brightness_, 1, offsetof(PerVertexData, brightness));
+	Gl::vertex_attrib_pointer(
+	   attr_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, gl_x));
+	Gl::vertex_attrib_pointer(
+	   attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
+	Gl::vertex_attrib_pointer(
+	   attr_brightness_, 1, sizeof(PerVertexData), offsetof(PerVertexData, brightness));
 
 	glBindBuffer(GL_ARRAY_BUFFER, 0);
 
@@ -233,6 +256,8 @@
 
 	glUniform1i(u_texture_, 0);
 
+	glUniform1f(u_z_value_, z_value);
+
 	glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
 
 	glDisableVertexAttribArray(attr_position_);

=== modified file 'src/graphic/gl/road_program.h'
--- src/graphic/gl/road_program.h	2015-02-08 18:16:41 +0000
+++ src/graphic/gl/road_program.h	2015-02-21 07:13:48 +0000
@@ -37,9 +37,9 @@
 	RoadProgram();
 	~RoadProgram();
 
-	// Draws the roads. The 'surface' is needed to convert from pixel space to
-	// GL space.
-	void draw(const Surface& surface, const FieldsToDraw& fields_to_draw);
+	// Draws the roads. The dimensions of the renderbuffer are needed to convert from pixel to GL
+	// space.
+	void draw(int renderbuffer_width, int renderbuffer_height, const FieldsToDraw& fields_to_draw, float z_value);
 
 private:
 	struct PerVertexData {
@@ -54,15 +54,16 @@
 	// Adds a road from 'start' to 'end' to be rendered in this frame using the
 	// correct texture for 'road_type'.
 	enum Direction {kEast, kSouthEast, kSouthWest};
-	void add_road(const Surface& surface,
+	void add_road(int renderbuffer_width,
+	              int renderbuffer_height,
 	              const FieldsToDraw::Field& start,
 	              const FieldsToDraw::Field& end,
 	              const Widelands::RoadType road_type,
 	              const Direction direction,
-					  int* gl_texture);
+	              int* gl_texture);
 
 	// The buffer that will contain 'vertices_' for rendering.
-	Gl::Buffer gl_array_buffer_;
+	Gl::NewBuffer<PerVertexData> gl_array_buffer_;
 
 	// The program used for drawing the roads.
 	Gl::Program gl_program_;
@@ -74,6 +75,7 @@
 
 	// Uniforms.
 	GLint u_texture_;
+	GLint u_z_value_;
 
 	// All vertices that get rendered this frame.
 	std::vector<PerVertexData> vertices_;

=== modified file 'src/graphic/gl/terrain_program.cc'
--- src/graphic/gl/terrain_program.cc	2014-11-28 05:40:53 +0000
+++ src/graphic/gl/terrain_program.cc	2015-02-21 07:13:48 +0000
@@ -40,6 +40,8 @@
 attribute vec2 attr_texture_offset;
 attribute vec2 attr_texture_position;
 
+uniform float u_z_value;
+
 // Output of vertex shader.
 varying float var_brightness;
 varying vec2 var_texture_offset;
@@ -49,7 +51,7 @@
 	var_texture_position = attr_texture_position;
 	var_brightness = attr_brightness;
 	var_texture_offset = attr_texture_offset;
-	gl_Position = vec4(attr_position, 0., 1.);
+	gl_Position = vec4(attr_position, u_z_value, 1.);
 }
 )";
 
@@ -64,8 +66,12 @@
 varying vec2 var_texture_offset;
 
 void main() {
+	// The arbitrary multiplication by 0.99 makes sure that we never sample
+	// outside of the texture in the texture atlas - this means non-perfect
+	// pixel mapping of textures to the screen, but we are pretty meh about that
+	// here.
 	vec4 clr = texture2D(u_terrain_texture,
-			var_texture_offset + u_texture_dimensions * fract(var_texture_position));
+			var_texture_offset + u_texture_dimensions * fract(var_texture_position) * 0.99);
 	clr.rgb *= var_brightness;
 	gl_FragColor = clr;
 }
@@ -83,9 +89,10 @@
 
 	u_terrain_texture_ = glGetUniformLocation(gl_program_.object(), "u_terrain_texture");
 	u_texture_dimensions_ = glGetUniformLocation(gl_program_.object(), "u_texture_dimensions");
+	u_z_value_ = glGetUniformLocation(gl_program_.object(), "u_z_value");
 }
 
-void TerrainProgram::gl_draw(int gl_texture, float texture_w, float texture_h) {
+void TerrainProgram::gl_draw(int gl_texture, float texture_w, float texture_h, float z_value) {
 	glUseProgram(gl_program_.object());
 
 	glEnableVertexAttribArray(attr_brightness_);
@@ -93,30 +100,23 @@
 	glEnableVertexAttribArray(attr_texture_offset_);
 	glEnableVertexAttribArray(attr_texture_position_);
 
-	glBindBuffer(GL_ARRAY_BUFFER, gl_array_buffer_.object());
-	glBufferData(GL_ARRAY_BUFFER,
-	             sizeof(TerrainProgram::PerVertexData) * vertices_.size(),
-	             vertices_.data(),
-	             GL_STREAM_DRAW);
+	gl_array_buffer_.bind();
+	gl_array_buffer_.update(vertices_);
 
-	const auto set_attrib_pointer = [](const int vertex_index, int num_items, int offset) {
-		glVertexAttribPointer(vertex_index,
-		                      num_items,
-		                      GL_FLOAT,
-		                      GL_FALSE,
-		                      sizeof(TerrainProgram::PerVertexData),
-		                      reinterpret_cast<void*>(offset));
-	};
-	set_attrib_pointer(attr_brightness_, 1, offsetof(PerVertexData, brightness));
-	set_attrib_pointer(attr_position_, 2, offsetof(PerVertexData, gl_x));
-	set_attrib_pointer(attr_texture_offset_, 2, offsetof(PerVertexData, texture_offset_x));
-	set_attrib_pointer(attr_texture_position_, 2, offsetof(PerVertexData, texture_x));
+	Gl::vertex_attrib_pointer(
+	   attr_brightness_, 1, sizeof(PerVertexData), offsetof(PerVertexData, brightness));
+	Gl::vertex_attrib_pointer(attr_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, gl_x));
+	Gl::vertex_attrib_pointer(
+	   attr_texture_offset_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_offset_x));
+	Gl::vertex_attrib_pointer(
+	   attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
 
 	glBindBuffer(GL_ARRAY_BUFFER, 0);
 
 	glActiveTexture(GL_TEXTURE0);
 	glBindTexture(GL_TEXTURE_2D, gl_texture);
 
+	glUniform1f(u_z_value_, z_value);
 	glUniform1i(u_terrain_texture_, 0);
 	glUniform2f(u_texture_dimensions_, texture_w, texture_h);
 
@@ -146,7 +146,8 @@
 
 void TerrainProgram::draw(uint32_t gametime,
                           const DescriptionMaintainer<TerrainDescription>& terrains,
-                          const FieldsToDraw& fields_to_draw) {
+                          const FieldsToDraw& fields_to_draw,
+                          float z_value) {
 	// This method expects that all terrains have the same dimensions and that
 	// all are packed into the same texture atlas, i.e. all are in the same GL
 	// texture. It does not check for this invariance for speeds sake.
@@ -170,7 +171,7 @@
 		   fields_to_draw.calculate_index(field.fx + (field.fy & 1) - 1, field.fy + 1);
 		if (bln_index != -1) {
 			const FloatPoint texture_offset =
-			   terrains.get_unmutable(field.ter_d).get_texture(gametime).texture_coordinates().top_left();
+			   terrains.get_unmutable(field.ter_d).get_texture(gametime).texture_coordinates().origin();
 			add_vertex(fields_to_draw.at(current_index), texture_offset);
 			add_vertex(fields_to_draw.at(bln_index), texture_offset);
 			add_vertex(fields_to_draw.at(brn_index), texture_offset);
@@ -180,7 +181,7 @@
 		const int rn_index = fields_to_draw.calculate_index(field.fx + 1, field.fy);
 		if (rn_index != -1) {
 			const FloatPoint texture_offset =
-			   terrains.get_unmutable(field.ter_r).get_texture(gametime).texture_coordinates().top_left();
+			   terrains.get_unmutable(field.ter_r).get_texture(gametime).texture_coordinates().origin();
 			add_vertex(fields_to_draw.at(current_index), texture_offset);
 			add_vertex(fields_to_draw.at(brn_index), texture_offset);
 			add_vertex(fields_to_draw.at(rn_index), texture_offset);
@@ -188,5 +189,8 @@
 	}
 
 	const Texture& texture = terrains.get_unmutable(0).get_texture(0);
-	gl_draw(texture.get_gl_texture(), texture.texture_coordinates().w, texture.texture_coordinates().h);
+	gl_draw(texture.get_gl_texture(),
+	        texture.texture_coordinates().w,
+	        texture.texture_coordinates().h,
+	        z_value);
 }

=== modified file 'src/graphic/gl/terrain_program.h'
--- src/graphic/gl/terrain_program.h	2014-11-27 18:26:29 +0000
+++ src/graphic/gl/terrain_program.h	2015-02-21 07:13:48 +0000
@@ -35,8 +35,10 @@
 	TerrainProgram();
 
 	// Draws the terrain.
-	void draw(uint32_t gametime, const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
-	          const FieldsToDraw& fields_to_draw);
+	void draw(uint32_t gametime,
+	          const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
+	          const FieldsToDraw& fields_to_draw,
+	          float z_value);
 
 private:
 	struct PerVertexData {
@@ -50,7 +52,7 @@
 	};
 	static_assert(sizeof(PerVertexData) == 28, "Wrong padding.");
 
-	void gl_draw(int gl_texture, float texture_w, float texture_h);
+	void gl_draw(int gl_texture, float texture_w, float texture_h, float z_value);
 
 	// Adds a vertex to the end of vertices with data from 'field' and 'texture_coordinates'.
 	void add_vertex(const FieldsToDraw::Field& field, const FloatPoint& texture_coordinates);
@@ -59,7 +61,7 @@
 	Gl::Program gl_program_;
 
 	// The buffer that will contain 'vertices_' for rendering.
-	Gl::Buffer gl_array_buffer_;
+	Gl::NewBuffer<PerVertexData> gl_array_buffer_;
 
 	// Attributes.
 	GLint attr_brightness_;
@@ -70,6 +72,7 @@
 	// Uniforms.
 	GLint u_terrain_texture_;
 	GLint u_texture_dimensions_;
+	GLint u_z_value_;
 
 	// Objects below are kept around to avoid memory allocations on each frame.
 	// They could theoretically also be recreated.

=== modified file 'src/graphic/gl/utils.cc'
--- src/graphic/gl/utils.cc	2014-09-27 18:53:55 +0000
+++ src/graphic/gl/utils.cc	2015-02-21 07:13:48 +0000
@@ -21,8 +21,6 @@
 #include <memory>
 #include <string>
 
-#include <SDL_video.h>
-
 #include "base/log.h"
 #include "base/wexception.h"
 
@@ -41,40 +39,8 @@
 	return "unknown";
 }
 
-// Creates one OpenGL buffer.
-GLuint create_buffer() {
-	GLuint buffer = 0;
-	glGenBuffers(1, &buffer);
-	return buffer;
-}
-
 }  // namespace
 
-/**
- * \return the standard 32-bit RGBA format that we use in OpenGL
- */
-const SDL_PixelFormat & gl_rgba_format()
-{
-	static SDL_PixelFormat format;
-	static bool init = false;
-	if (init)
-		return format;
-
-	init = true;
-	memset(&format, 0, sizeof(format));
-	format.BitsPerPixel = 32;
-	format.BytesPerPixel = 4;
-	format.Rmask = 0x000000ff;
-	format.Gmask = 0x0000ff00;
-	format.Bmask = 0x00ff0000;
-	format.Amask = 0xff000000;
-	format.Rshift = 0;
-	format.Gshift = 8;
-	format.Bshift = 16;
-	format.Ashift = 24;
-	return format;
-}
-
 GLenum _handle_glerror(const char * file, unsigned int line)
 {
 	GLenum err = glGetError();
@@ -162,18 +128,6 @@
 	}
 }
 
-Buffer::Buffer() : buffer_object_(create_buffer()) {
-	if (!buffer_object_) {
-		throw wexception("Could not create GL program.");
-	}
-}
-
-Buffer::~Buffer() {
-	if (buffer_object_) {
-		glDeleteBuffers(1, &buffer_object_);
-	}
-}
-
 Program::Program() : program_object_(glCreateProgram()) {
 	if (!program_object_) {
 		throw wexception("Could not create GL program.");
@@ -212,4 +166,21 @@
 	}
 }
 
+void vertex_attrib_pointer(int vertex_index, int num_items, int stride, int offset) {
+	glVertexAttribPointer(
+	   vertex_index, num_items, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<void*>(offset));
+}
+
+void swap_rows(const int width, const int height, const int pitch, const int bpp, uint8_t* pixels) {
+	uint8_t* begin_row = pixels;
+	uint8_t* end_row = pixels + pitch * (height - 1);
+	while (begin_row < end_row) {
+		for (int x = 0; x < width * bpp; ++x) {
+			std::swap(begin_row[x], end_row[x]);
+		}
+		begin_row += pitch;
+		end_row -= pitch;
+	}
+}
+
 }  // namespace Gl

=== modified file 'src/graphic/gl/utils.h'
--- src/graphic/gl/utils.h	2014-11-08 13:59:33 +0000
+++ src/graphic/gl/utils.h	2015-02-21 07:13:48 +0000
@@ -20,19 +20,19 @@
 #define WL_GRAPHIC_GL_UTILS_H
 
 #include <memory>
+#include <vector>
 
 #include <stdint.h>
 
+#include "base/log.h"
 #include "base/macros.h"
+#include "base/wexception.h"
 #include "graphic/gl/system_headers.h"
 
-struct SDL_PixelFormat;
-
 namespace Gl {
 
 class Shader;
 
-const SDL_PixelFormat & gl_rgba_format();
 GLenum _handle_glerror(const char * file, unsigned int line);
 
 // Thin wrapper around a OpenGL program object to ensure proper cleanup. Throws
@@ -59,22 +59,54 @@
 };
 
 // Thin wrapper around a OpenGL buffer object to ensure proper cleanup. Throws
-// on all errors.
-class Buffer {
+// on all errors. Also grows the server memory only when needed.
+template<typename T>
+class NewBuffer {
 public:
-	Buffer();
-	~Buffer();
-
-	GLuint object() const {
-		return buffer_object_;
+	NewBuffer() : buffer_size_(0) {
+		glGenBuffers(1, &object_);
+		if (!object_) {
+			throw wexception("Could not create GL program.");
+		}
+	}
+
+	~NewBuffer() {
+		if (object_) {
+			glDeleteBuffers(1, &object_);
+		}
+	}
+
+	// Calls glBindBuffer on the underlying buffer data.
+	void bind() const {
+		glBindBuffer(GL_ARRAY_BUFFER, object_);
+	}
+
+
+	// Copies 'elements' into the buffer. If the buffer is too small to hold the
+	// data, it is reallocated. Does not check if the buffer is already bound.
+	void update(const std::vector<T>& items) {
+		if (buffer_size_ < items.size()) {
+			glBufferData(GL_ARRAY_BUFFER, items.size() * sizeof(T), items.data(), GL_DYNAMIC_DRAW);
+			buffer_size_ = items.size();
+		} else {
+			glBufferSubData(GL_ARRAY_BUFFER, 0, items.size() * sizeof(T), items.data());
+		}
 	}
 
 private:
-	const GLuint buffer_object_;
+	GLuint object_;
+	size_t buffer_size_;  // In number of elements.
 
-	DISALLOW_COPY_AND_ASSIGN(Buffer);
+	DISALLOW_COPY_AND_ASSIGN(NewBuffer);
 };
 
+// Calls glVertexAttribPointer.
+void vertex_attrib_pointer(int vertex_index, int num_items, int stride, int offset);
+
+// Swap order of rows in m_pixels, to compensate for the upside-down nature of the
+// OpenGL coordinate system.
+void swap_rows(int width, int height, int pitch, int bpp, uint8_t* pixels);
+
 }  // namespace Gl
 
 /**

=== modified file 'src/graphic/graphic.cc'
--- src/graphic/graphic.cc	2014-12-27 09:59:12 +0000
+++ src/graphic/graphic.cc	2015-02-21 07:13:48 +0000
@@ -29,6 +29,7 @@
 #include "graphic/gl/system_headers.h"
 #include "graphic/image.h"
 #include "graphic/image_io.h"
+#include "graphic/render_queue.h"
 #include "graphic/rendertarget.h"
 #include "graphic/screen.h"
 #include "graphic/texture.h"
@@ -70,6 +71,7 @@
 	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
 	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
+	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
 
 	log("Graphics: Try to set Videomode %ux%u\n", m_window_mode_width, m_window_mode_height);
 	m_sdl_window = SDL_CreateWindow("Widelands Window",
@@ -111,12 +113,11 @@
 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glInt);
 	log("Graphics: OpenGL: Max texture size: %u\n", glInt);
 
-	SDL_GL_SetSwapInterval(1);
-
 	glDrawBuffer(GL_BACK);
 
-	glDisable(GL_DEPTH_TEST);
-	glEnable(GL_TEXTURE_2D);
+	glEnable(GL_DEPTH_TEST);
+	glDepthFunc(GL_LEQUAL);
+
 	glEnable(GL_BLEND);
 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
@@ -244,9 +245,8 @@
 /**
  * Returns true if parts of the screen have been marked for refreshing.
 */
-bool Graphic::need_update() const
-{
-	return  m_update;
+bool Graphic::need_update() const {
+	return m_update;
 }
 
 /**
@@ -256,6 +256,8 @@
 */
 void Graphic::refresh()
 {
+	RenderQueue::instance().draw(screen_->width(), screen_->height());
+
 	// Setting the window size immediately after going out of fullscreen does
 	// not work properly. We work around this issue by resizing the window in
 	// refresh() when in window mode.

=== modified file 'src/graphic/image_cache.cc'
--- src/graphic/image_cache.cc	2014-12-07 20:52:55 +0000
+++ src/graphic/image_cache.cc	2015-02-21 07:13:48 +0000
@@ -21,13 +21,47 @@
 
 #include <cassert>
 #include <memory>
+#include <set>
 #include <string>
 
-#include "base/log.h"
+#include <SDL.h>
+#include <boost/format.hpp>
+
 #include "graphic/image.h"
 #include "graphic/image_io.h"
 #include "graphic/texture.h"
 
+namespace  {
+
+constexpr int kBiggestAreaForCompactification = 250 * 250;
+
+}  // namespace
+ImageCache::ProxyImage::ProxyImage(std::unique_ptr<const Image> image) : image_(std::move(image)) {
+}
+
+const Image& ImageCache::ProxyImage::image() {
+	return *image_;
+}
+
+void ImageCache::ProxyImage::set_image(std::unique_ptr<const Image> image) {
+	image_ = std::move(image);
+}
+
+int ImageCache::ProxyImage::width() const {
+	return image_->width();
+}
+
+int ImageCache::ProxyImage::height() const {
+	return image_->height();
+}
+int ImageCache::ProxyImage::get_gl_texture() const {
+	return image_->get_gl_texture();
+}
+
+const FloatRect& ImageCache::ProxyImage::texture_coordinates() const {
+	return image_->texture_coordinates();
+}
+
 ImageCache::ImageCache() {
 }
 
@@ -41,15 +75,45 @@
 const Image* ImageCache::insert(const std::string& hash, std::unique_ptr<const Image> image) {
 	assert(!has(hash));
 	const Image* return_value = image.get();
-	images_.insert(make_pair(hash, std::move(image)));
+	images_.insert(make_pair(hash, std::unique_ptr<ProxyImage>(new ProxyImage(std::move(image)))));
 	return return_value;
 }
 
 const Image* ImageCache::get(const std::string& hash) {
 	ImageMap::const_iterator it = images_.find(hash);
 	if (it == images_.end()) {
-		images_.insert(make_pair(hash, load_image(hash)));
+		images_.insert(
+		   make_pair(hash, std::unique_ptr<ProxyImage>(new ProxyImage(load_image(hash)))));
 		return get(hash);
 	}
 	return it->second.get();
 }
+
+void ImageCache::compactify() {
+	TextureAtlas texture_atlas;
+
+	std::vector<std::string> hashes;
+	for (const auto& pair : images_) {
+		const auto& image = pair.second->image();
+		if (image.width() * image.height() > kBiggestAreaForCompactification) {
+			continue;
+		}
+
+		texture_atlas.add(image);
+		hashes.push_back(pair.first);
+	}
+
+	std::vector<std::unique_ptr<Texture>> new_textures;
+
+	// TODO(sirver): Limit the size of the texture atlas to a max GL texture
+	// size. This might return more than one packed image. Make sure that the
+	// code works also for small max texture sizes.
+	texture_atlases_.emplace_back(texture_atlas.pack(&new_textures));
+
+	assert(new_textures.size() == hashes.size());
+	std::set<int> gl_textures;
+	for (size_t i = 0; i < hashes.size(); ++i) {
+		gl_textures.insert(new_textures[i]->get_gl_texture());
+		images_[hashes[i]]->set_image(std::move(new_textures[i]));
+	}
+}

=== modified file 'src/graphic/image_cache.h'
--- src/graphic/image_cache.h	2014-12-07 15:41:39 +0000
+++ src/graphic/image_cache.h	2015-02-21 07:13:48 +0000
@@ -23,11 +23,13 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <boost/utility.hpp>
 
 #include "base/macros.h"
 #include "graphic/image.h"
+#include "graphic/texture_atlas.h"
 
 // For historic reasons, most part of the Widelands code base expect that an
 // Image stays valid for the whole duration of the program run. This class is
@@ -53,9 +55,33 @@
 	// Returns true if the given hash is stored in the cache.
 	bool has(const std::string& hash) const;
 
+	// For debug only: Takes all images that are in the ImageCache right now and
+	// puts them into one huge texture atlas.
+	void compactify();
+
 private:
-	using ImageMap = std::map<std::string, std::unique_ptr<const Image>>;
-
+	// We return a wrapped Image so that we can swap out the pointer to the
+	// image under our user. This can happen when we move an Image from a stand
+	// alone texture into being a subrect of a texture atlas.
+	class ProxyImage : public Image {
+	public:
+		ProxyImage(std::unique_ptr<const Image> image);
+
+		const Image& image();
+		void set_image(std::unique_ptr<const Image> image);
+
+		int width() const override;
+		int height() const override;
+		int get_gl_texture() const override;
+		const FloatRect& texture_coordinates() const override;
+
+	private:
+		std::unique_ptr<const Image> image_;
+	};
+
+	using ImageMap = std::map<std::string, std::unique_ptr<ProxyImage>>;
+
+	std::vector<std::unique_ptr<Texture>> texture_atlases_;
 	ImageMap images_;  /// hash of cached filename/image pairs
 
 	DISALLOW_COPY_AND_ASSIGN(ImageCache);

=== modified file 'src/graphic/image_io.cc'
--- src/graphic/image_io.cc	2014-12-07 21:34:11 +0000
+++ src/graphic/image_io.cc	2015-02-21 07:13:48 +0000
@@ -25,6 +25,7 @@
 #include <SDL_image.h>
 #include <png.h>
 
+#include "base/log.h"
 #include "base/wexception.h"
 #include "graphic/texture.h"
 #include "io/fileread.h"
@@ -130,7 +131,6 @@
 		std::unique_ptr<png_byte[]> row(new png_byte[row_size]);
 
 		// Write each row
-		const SDL_PixelFormat& fmt = texture->format();
 		texture->lock();
 
 		// Write each row
@@ -138,7 +138,7 @@
 		if (color_type == ColorType::RGB) {
 			for (uint32_t y = 0; y < surf_h; ++y) {
 				for (uint32_t x = 0; x < surf_w; ++x) {
-					color.set(fmt, texture->get_pixel(x, y));
+					color = texture->get_pixel(x, y);
 					row[3 * x] = color.r;
 					row[3 * x + 1] = color.g;
 					row[3 * x + 2] = color.b;
@@ -148,7 +148,7 @@
 		} else {
 			for (uint32_t y = 0; y < surf_h; ++y) {
 				for (uint32_t x = 0; x < surf_w; ++x) {
-					color.set(fmt, texture->get_pixel(x, y));
+					color = texture->get_pixel(x, y);
 					row[4 * x] = color.r;
 					row[4 * x + 1] = color.g;
 					row[4 * x + 2] = color.b;

=== modified file 'src/graphic/minimap_renderer.cc'
--- src/graphic/minimap_renderer.cc	2014-12-07 21:34:11 +0000
+++ src/graphic/minimap_renderer.cc	2015-02-21 07:13:48 +0000
@@ -38,68 +38,49 @@
 
 namespace  {
 
+const RGBColor kWhite(255, 255, 255);
+
 // Blend two colors.
-inline uint32_t blend_color
-	(const SDL_PixelFormat& format, uint32_t clr1, uint8_t r2, uint8_t g2, uint8_t b2)
-{
-	uint8_t r1, g1, b1;
-	SDL_GetRGB(clr1, &const_cast<SDL_PixelFormat &>(format), &r1, &g1, &b1);
-	return
-		SDL_MapRGB
-			(&const_cast<SDL_PixelFormat &>(format), (r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2);
+inline RGBColor blend_color(const RGBColor& c1, const RGBColor& c2) {
+	return RGBColor((c1.r + c2.r) / 2, (c1.g + c2.g) / 2, (c1.b + c2.b) / 2);
 }
 
 // Returns the color to be used in the minimap for the given field.
-inline uint32_t calc_minimap_color
-	(const SDL_PixelFormat& format, const Widelands::EditorGameBase& egbase,
-	 const Widelands::FCoords& f, MiniMapLayer layers, Widelands::PlayerNumber owner,
-	 bool see_details)
-{
-	uint32_t pixelcolor = 0;
-
+inline RGBColor calc_minimap_color(const Widelands::EditorGameBase& egbase,
+                                   const Widelands::FCoords& f,
+                                   MiniMapLayer layers,
+                                   Widelands::PlayerNumber owner,
+                                   bool see_details) {
+	RGBColor color;
 	if (layers & MiniMapLayer::Terrain) {
-		const RGBColor& color =  egbase.world().terrain_descr(f.field->terrain_d()).get_minimap_color(
+		color = egbase.world().terrain_descr(f.field->terrain_d()).get_minimap_color(
 		   f.field->get_brightness());
-
-		pixelcolor = SDL_MapRGBA(&format, color.r, color.g, color.b, 255);
 	}
 
 	if (layers & MiniMapLayer::Owner) {
-		if (0 < owner) { //  If owned, get the player's color...
-			const RGBColor & player_color = egbase.player(owner).get_playercolor();
-
-			//  ...and add the player's color to the old color.
-			pixelcolor = blend_color
-				(format,
-				 pixelcolor,
-				 player_color.r,  player_color.g, player_color.b);
+		if (0 < owner) {
+			color = blend_color(color, egbase.player(owner).get_playercolor());
 		}
 	}
 
 	if (see_details) {
 		// if ownership layer is displayed, it creates enough contrast to
 		// visualize objects using white color.
-		// Otherwise, a more contrasting color may be needed:
-		// * winterland -> orange
 
 		if (upcast(PlayerImmovable const, immovable, f.field->get_immovable())) {
 			if ((layers & MiniMapLayer::Road) && dynamic_cast<Road const *>(immovable)) {
-				pixelcolor = blend_color(format, pixelcolor, 255, 255, 255);
+				color = blend_color(color, kWhite);
 			}
 
-			if
-				(((layers & MiniMapLayer::Flag) && dynamic_cast<Flag const *>(immovable))
-				 ||
-				 ((layers & MiniMapLayer::Building)
-				  &&
-				  dynamic_cast<Widelands::Building const *>(immovable)))
-			{
-				pixelcolor = SDL_MapRGB(&const_cast<SDL_PixelFormat&>(format), 255, 255, 255);
+			if (((layers & MiniMapLayer::Flag) && dynamic_cast<Flag const*>(immovable)) ||
+			    ((layers & MiniMapLayer::Building) &&
+			     dynamic_cast<Widelands::Building const*>(immovable))) {
+				color = kWhite;
 			}
 		}
 	}
 
-	return pixelcolor;
+	return color;
 }
 
 // Draws the dotted frame border onto the minimap.
@@ -149,15 +130,13 @@
 }
 
 // Does the actual work of drawing the minimap.
-void draw_minimap_int
-	(Texture* texture, const Widelands::EditorGameBase& egbase,
-	 const Widelands::Player* player, const Point& viewpoint, MiniMapLayer layers)
-{
-	const Widelands::Map & map = egbase.map();
+void draw_minimap_int(Texture* texture,
+                      const Widelands::EditorGameBase& egbase,
+                      const Widelands::Player* player,
+                      const Point& viewpoint,
+                      MiniMapLayer layers) {
+	const Widelands::Map& map = egbase.map();
 
-	uint8_t* const pixels = texture->get_pixels();
-	const SDL_PixelFormat& format = texture->format();
-	const uint16_t pitch = texture->get_pitch();
 	const uint16_t surface_h = texture->height();
 	const uint16_t surface_w = texture->width();
 
@@ -168,90 +147,65 @@
 	const int32_t mapwidth = egbase.get_map().get_width();
 	const int32_t mapheight = map.get_height();
 
-	Point ptopleft; // top left point of the current display frame
+	Point ptopleft;  // top left point of the current display frame
 	ptopleft.x = viewpoint.x + mapwidth / 2 - xsize;
-	if (ptopleft.x < 0) ptopleft.x += mapwidth;
+	if (ptopleft.x < 0)
+		ptopleft.x += mapwidth;
 	ptopleft.y = viewpoint.y + mapheight / 2 - ysize;
-	if (ptopleft.y < 0) ptopleft.y += mapheight;
+	if (ptopleft.y < 0)
+		ptopleft.y += mapheight;
 
-	Point pbottomright; // bottom right point of the current display frame
+	Point pbottomright;  // bottom right point of the current display frame
 	pbottomright.x = viewpoint.x + mapwidth / 2 + xsize;
-	if (pbottomright.x >= mapwidth) pbottomright.x -= mapwidth;
+	if (pbottomright.x >= mapwidth)
+		pbottomright.x -= mapwidth;
 	pbottomright.y = viewpoint.y + mapheight / 2 + ysize;
-	if (pbottomright.y >= mapheight) pbottomright.y -= mapheight;
+	if (pbottomright.y >= mapheight)
+		pbottomright.y -= mapheight;
 
 	uint32_t modx = pbottomright.x % 2;
 	uint32_t mody = pbottomright.y % 2;
 
-	if (!player || player->see_all()) {
-			for (uint32_t y = 0; y < surface_h; ++y) {
-			uint8_t * pix = pixels + y * pitch;
-			Widelands::FCoords f
-				(Widelands::Coords
-					(viewpoint.x, viewpoint.y + (layers & MiniMapLayer::Zoom2 ? y / 2 : y)));
-			map.normalize_coords(f);
-			f.field = &map[f];
-			Widelands::MapIndex i = Widelands::Map::get_index(f, mapwidth);
-			for (uint32_t x = 0; x < surface_w; ++x, pix += sizeof(uint32_t)) {
-				if (x % 2 || !(layers & MiniMapLayer::Zoom2))
-					move_r(mapwidth, f, i);
-
-				if ((layers & MiniMapLayer::ViewWindow) &&
-				    is_minimap_frameborder(
-				       f, ptopleft, pbottomright, mapwidth, mapheight, modx, mody)) {
-					*reinterpret_cast<uint32_t *>(pix) = static_cast<uint32_t>
-						(SDL_MapRGB(&const_cast<SDL_PixelFormat &>(format), 255, 0, 0));
-				} else {
-					*reinterpret_cast<uint32_t *>(pix) = static_cast<uint32_t>
-						(calc_minimap_color
-							(format, egbase, f, layers, f.field->get_owned_by(), true));
+	for (uint32_t y = 0; y < surface_h; ++y) {
+		Widelands::FCoords f(
+		   Widelands::Coords(viewpoint.x, viewpoint.y + (layers & MiniMapLayer::Zoom2 ? y / 2 : y)));
+		map.normalize_coords(f);
+		f.field = &map[f];
+		Widelands::MapIndex i = Widelands::Map::get_index(f, mapwidth);
+		for (uint32_t x = 0; x < surface_w; ++x) {
+			if (x % 2 || !(layers & MiniMapLayer::Zoom2))
+				move_r(mapwidth, f, i);
+
+			RGBColor pixel_color;
+			if ((layers & MiniMapLayer::ViewWindow) &&
+			    is_minimap_frameborder(f, ptopleft, pbottomright, mapwidth, mapheight, modx, mody)) {
+				pixel_color = RGBColor(255, 0, 0);
+			} else {
+				uint16_t vision =
+				   0;  // See Player::Field::Vision: 1 if seen once, > 1 if seen right now.
+				Widelands::PlayerNumber owner = 0;
+				if (player == nullptr || player->see_all()) {
+					vision = 2;  // Seen right now.
+					owner = f.field->get_owned_by();
+				} else if (player != nullptr) {
+					const auto& field = player->fields()[i];
+					vision = field.vision;
+					owner = field.owner;
+				}
+
+				if (vision > 0) {
+					assert(owner != 0);
+					pixel_color = calc_minimap_color(egbase, f, layers, owner, vision > 1);
 				}
 			}
-		}
-	} else {
-		Widelands::Player::Field const * const player_fields = player->fields();
-		for (uint32_t y = 0; y < surface_h; ++y) {
-			uint8_t * pix = pixels + y * pitch;
-			Widelands::FCoords f
-				(Widelands::Coords
-			 		(viewpoint.x, viewpoint.y +
-			 		 (layers & MiniMapLayer::Zoom2 ? y / 2 : y)));
-			map.normalize_coords(f);
-			f.field = &map[f];
-			Widelands::MapIndex i = Widelands::Map::get_index(f, mapwidth);
-			for (uint32_t x = 0; x < surface_w; ++x, pix += sizeof(uint32_t)) {
-				if (x % 2 || !(layers & MiniMapLayer::Zoom2))
-					move_r(mapwidth, f, i);
-
-				if ((layers & MiniMapLayer::ViewWindow) &&
-				    is_minimap_frameborder(
-				       f, ptopleft, pbottomright, mapwidth, mapheight, modx, mody)) {
-					*reinterpret_cast<uint32_t *>(pix) = static_cast<uint32_t>
-						(SDL_MapRGB
-							(&const_cast<SDL_PixelFormat &>(format), 255, 0, 0));
-				} else {
-					const Widelands::Player::Field & player_field = player_fields[i];
-					Widelands::Vision const vision = player_field.vision;
-
-					*reinterpret_cast<uint32_t *>(pix) =
-						static_cast<uint32_t>
-						(vision ?
-						 calc_minimap_color
-						 	(format,
-						 	 egbase,
-						 	 f,
-						 	 layers,
-						 	 player_field.owner,
-						 	 1 < vision)
-						 :
-						 SDL_MapRGB(&const_cast<SDL_PixelFormat &>(format), 0, 0, 0));
-				}
+
+			if (pixel_color.r != 0 || pixel_color.g != 0 || pixel_color.b != 0) {
+				texture->set_pixel(x, y, pixel_color);
 			}
 		}
 	}
 }
 
-
 }  // namespace
 
 std::unique_ptr<Texture> draw_minimap(const EditorGameBase& egbase,
@@ -265,17 +219,16 @@
 	const int16_t map_w = (layers & MiniMapLayer::Zoom2) ? map.get_width() * 2 : map.get_width();
 	const int16_t map_h = (layers & MiniMapLayer::Zoom2) ? map.get_height() * 2 : map.get_height();
 
-	Texture* texture = new Texture(map_w, map_h);
+	std::unique_ptr<Texture> texture(new Texture(map_w, map_h));
 	assert(texture->format().BytesPerPixel == sizeof(uint32_t));
 
-	fill_rect(Rect(0, 0, texture->width(), texture->height()), RGBAColor(0, 0, 0, 255), texture);
+	texture->fill_rect(Rect(0, 0, texture->width(), texture->height()), RGBAColor(0, 0, 0, 255));
+
 	texture->lock();
-
-	draw_minimap_int(texture, egbase, player, viewpoint, layers);
-
+	draw_minimap_int(texture.get(), egbase, player, viewpoint, layers);
 	texture->unlock(Texture::Unlock_Update);
 
-	return std::unique_ptr<Texture>(texture);
+	return texture;
 }
 
 void write_minimap_image

=== added file 'src/graphic/render_queue.cc'
--- src/graphic/render_queue.cc	1970-01-01 00:00:00 +0000
+++ src/graphic/render_queue.cc	2015-02-21 07:13:48 +0000
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "graphic/render_queue.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/log.h"
+#include "base/rect.h"
+#include "base/wexception.h"
+#include "graphic/gl/blit_program.h"
+#include "graphic/gl/dither_program.h"
+#include "graphic/gl/draw_line_program.h"
+#include "graphic/gl/fill_rect_program.h"
+#include "graphic/gl/road_program.h"
+#include "graphic/gl/terrain_program.h"
+
+namespace {
+
+constexpr int kMaximumZValue = std::numeric_limits<uint16_t>::max();
+constexpr float kOpenGlZDelta = -2.f / kMaximumZValue;
+
+// Maps [0, kMaximumZValue] linearly to [1., -1.] for use in vertex shaders.
+inline float to_opengl_z(const int z) {
+	return -(2.f * z) / kMaximumZValue + 1.f;
+}
+
+// The key defines in which order we render things.
+//
+// For opaque objects, render order makes no difference in the final image, but
+//   - we batch up by program to have maximal batching.
+//   - and we want to render frontmost objects first, so that we do not render
+//     any pixel more than once.
+static_assert(RenderQueue::Program::HIGHEST_PROGRAM_ID <= 8,
+              "Need to change sorting keys.");  // 4 bits.
+
+uint64_t
+make_key_opaque(const uint64_t program_id, const uint64_t z_value, const uint64_t extra_value) {
+	assert(program_id < RenderQueue::Program::HIGHEST_PROGRAM_ID);
+	assert(z_value < std::numeric_limits<uint16_t>::max());
+
+	// TODO(sirver): As a higher priority for sorting then z value, texture
+	// could be used here. This allows for more batching of GL calls, but in my
+	// tests hardly made a difference for Widelands..
+	uint64_t sort_z_value = std::numeric_limits<uint16_t>::max() - z_value;
+	// IIII0000 EEEEEEEE EEEEEEEE EEEEEEEE EEEEEEEE ZZZZZZZZ ZZZZZZZZ
+	return (program_id << 60) | (extra_value << 16) | (sort_z_value);
+}
+
+// For blended objects, we need to render furthest away objects first, and we
+// do not update the z-buffer. This guarantees that the image is correct.
+//   - if z value is the same, we order by program second to have potential batching.
+uint64_t
+make_key_blended(const uint64_t program_id, const uint64_t z_value, const uint64_t extra_value) {
+	assert(program_id < RenderQueue::Program::HIGHEST_PROGRAM_ID);
+	assert(z_value < std::numeric_limits<uint16_t>::max());
+
+	// Sort opaque objects increasing, alpha objects decreasing in order.
+	// ZZZZZZZZ ZZZZZZZZ IIII0000 EEEEEEEE EEEEEEEE EEEEEEEE EEEEEEEE
+	return (z_value << 40) | (program_id << 36) | extra_value;
+}
+
+// Construct 'args' used by the individual programs out of 'item'.
+inline void from_item(const RenderQueue::Item& item, VanillaBlitProgram::Arguments* args) {
+	args->texture = item.vanilla_blit_arguments.texture;
+	args->opacity = item.vanilla_blit_arguments.opacity;
+}
+
+inline void from_item(const RenderQueue::Item& item, MonochromeBlitProgram::Arguments* args) {
+	args->texture = item.monochrome_blit_arguments.texture;
+	args->blend = item.monochrome_blit_arguments.blend;
+}
+
+inline void from_item(const RenderQueue::Item& item, FillRectProgram::Arguments* args) {
+	args->color = item.rect_arguments.color;
+}
+
+inline void from_item(const RenderQueue::Item& item, BlendedBlitProgram::Arguments* args) {
+	args->texture = item.blended_blit_arguments.texture;
+	args->blend = item.blended_blit_arguments.blend;
+	args->mask = item.blended_blit_arguments.mask;
+}
+
+inline void from_item(const RenderQueue::Item& item, DrawLineProgram::Arguments* args) {
+	args->color = item.line_arguments.color;
+	args->line_width = item.line_arguments.line_width;
+}
+
+// Batches up as many items from 'items' that have the same 'program_id'.
+// Increases 'index' and returns an argument vector that can directly be passed
+// to the individual program.
+template <typename T>
+std::vector<T> batch_up(const RenderQueue::Program program_id,
+                        const std::vector<RenderQueue::Item>& items,
+                        size_t* index) {
+	std::vector<T> all_args;
+	while (*index < items.size()) {
+		const RenderQueue::Item& current_item = items.at(*index);
+		if (current_item.program_id != program_id) {
+			break;
+		}
+		all_args.emplace_back();
+		T& args = all_args.back();
+		args.destination_rect = current_item.destination_rect;
+		args.z_value = current_item.z_value;
+		args.blend_mode = current_item.blend_mode;
+		from_item(current_item, &args);
+		++(*index);
+	}
+	return all_args;
+}
+
+// Calls glScissor for the given 'rect' and enables GL_SCISSOR_TEST at
+// creation. Disables GL_SCISSOR_TEST at desctruction again.
+class ScopedScissor {
+public:
+	ScopedScissor(const FloatRect& rect);
+	~ScopedScissor();
+
+private:
+	DISALLOW_COPY_AND_ASSIGN(ScopedScissor);
+};
+
+ScopedScissor::ScopedScissor(const FloatRect& rect) {
+	glScissor(rect.x, rect.y, rect.w, rect.h);
+	glEnable(GL_SCISSOR_TEST);
+}
+
+ScopedScissor::~ScopedScissor() {
+	glDisable(GL_SCISSOR_TEST);
+}
+
+}  // namespace
+
+RenderQueue::RenderQueue()
+   : next_z_(1),
+     terrain_program_(new TerrainProgram()),
+     dither_program_(new DitherProgram()),
+     road_program_(new RoadProgram()) {
+}
+
+// static
+RenderQueue& RenderQueue::instance() {
+	static RenderQueue render_queue;
+	return render_queue;
+}
+
+void RenderQueue::enqueue(const Item& given_item) {
+	Item* item;
+	uint32_t extra_value = 0;
+
+	switch (given_item.program_id) {
+		case Program::BLIT:
+		   extra_value = given_item.vanilla_blit_arguments.texture.name;
+		 break;
+
+		case Program::BLIT_MONOCHROME:
+		   extra_value = given_item.monochrome_blit_arguments.texture.name;
+			break;
+
+		case Program::BLIT_BLENDED:
+		   extra_value = given_item.blended_blit_arguments.texture.name;
+			break;
+
+		case Program::LINE:
+		   extra_value = given_item.line_arguments.line_width;
+			break;
+
+		case Program::RECT:
+		case Program::TERRAIN_BASE:
+		case Program::TERRAIN_DITHER:
+		case Program::TERRAIN_ROAD:
+			/* all fallthroughs intended */
+			break;
+
+		default:
+		   throw wexception("Unknown given_item.program_id: %d", given_item.program_id);
+	}
+
+	if (given_item.blend_mode == BlendMode::Copy) {
+		opaque_items_.emplace_back(given_item);
+		item = &opaque_items_.back();
+		item->z_value = to_opengl_z(next_z_);
+		item->key = make_key_opaque(static_cast<uint64_t>(item->program_id), next_z_, extra_value);
+	} else {
+		blended_items_.emplace_back(given_item);
+		item = &blended_items_.back();
+		item->z_value = to_opengl_z(next_z_);
+		item->key = make_key_blended(static_cast<uint64_t>(item->program_id), next_z_, extra_value);
+	}
+	++next_z_;
+}
+
+void RenderQueue::draw(const int screen_width, const int screen_height) {
+	if (next_z_ >= kMaximumZValue) {
+		throw wexception("Too many drawn layers. Ran out of z-values.");
+	}
+
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	glViewport(0, 0, screen_width, screen_height);
+
+	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+	glDisable(GL_BLEND);
+
+	std::sort(opaque_items_.begin(), opaque_items_.end());
+	draw_items(opaque_items_);
+	opaque_items_.clear();
+
+	glEnable(GL_BLEND);
+	glDepthMask(GL_FALSE);
+
+	std::sort(blended_items_.begin(), blended_items_.end());
+	draw_items(blended_items_);
+	blended_items_.clear();
+
+	glDepthMask(GL_TRUE);
+
+	next_z_ = 1;
+}
+
+void RenderQueue::draw_items(const std::vector<Item>& items) {
+	size_t i = 0;
+	while (i < items.size()) {
+		const Item& item = items[i];
+		switch (item.program_id) {
+		case Program::BLIT:
+			VanillaBlitProgram::instance().draw(
+			   batch_up<VanillaBlitProgram::Arguments>(Program::BLIT, items, &i));
+		 break;
+
+		case Program::BLIT_MONOCHROME:
+			MonochromeBlitProgram::instance().draw(
+			   batch_up<MonochromeBlitProgram::Arguments>(Program::BLIT_MONOCHROME, items, &i));
+			break;
+
+		case Program::BLIT_BLENDED:
+			BlendedBlitProgram::instance().draw(
+			   batch_up<BlendedBlitProgram::Arguments>(Program::BLIT_BLENDED, items, &i));
+			break;
+
+		case Program::LINE:
+			DrawLineProgram::instance().draw(
+			   batch_up<DrawLineProgram::Arguments>(Program::LINE, items, &i));
+			break;
+
+		case Program::RECT:
+			FillRectProgram::instance().draw(
+			   batch_up<FillRectProgram::Arguments>(Program::RECT, items, &i));
+			break;
+
+		case Program::TERRAIN_BASE: {
+			ScopedScissor scoped_scissor(item.destination_rect);
+			terrain_program_->draw(item.terrain_arguments.gametime,
+			                       *item.terrain_arguments.terrains,
+			                       *item.terrain_arguments.fields_to_draw,
+			                       item.z_value);
+			++i;
+		} break;
+
+		case Program::TERRAIN_DITHER: {
+			ScopedScissor scoped_scissor(item.destination_rect);
+			dither_program_->draw(item.terrain_arguments.gametime,
+			                      *item.terrain_arguments.terrains,
+			                      *item.terrain_arguments.fields_to_draw,
+			                      item.z_value + kOpenGlZDelta);
+			++i;
+		} break;
+
+		case Program::TERRAIN_ROAD: {
+			ScopedScissor scoped_scissor(item.destination_rect);
+			road_program_->draw(item.terrain_arguments.renderbuffer_width,
+			                    item.terrain_arguments.renderbuffer_height,
+			                    *item.terrain_arguments.fields_to_draw,
+			                    item.z_value + 2 * kOpenGlZDelta);
+			++i;
+		} break;
+
+		default:
+			throw wexception("Unknown item.program_id: %d", item.program_id);
+		}
+	}
+}

=== added file 'src/graphic/render_queue.h'
--- src/graphic/render_queue.h	1970-01-01 00:00:00 +0000
+++ src/graphic/render_queue.h	2015-02-21 07:13:48 +0000
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_GRAPHIC_RENDER_QUEUE_H
+#define WL_GRAPHIC_RENDER_QUEUE_H
+
+#include <memory>
+#include <vector>
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/rect.h"
+#include "graphic/blend_mode.h"
+#include "graphic/color.h"
+#include "graphic/gl/blit_source.h"
+#include "graphic/gl/fields_to_draw.h"
+#include "logic/description_maintainer.h"
+#include "logic/world/terrain_description.h"
+
+class DitherProgram;
+class RoadProgram;
+class TerrainProgram;
+
+// The RenderQueue is a singleton implementing the concept of deferred
+// rendering: Every rendering call that pretends to draw onto the screen will
+// instead enqueue an item into the RenderQueue. The Graphic::refresh() will
+// then setup OpenGL to render onto the screen and then call
+// RenderQueue::draw() which will execute all the draw calls.
+//
+// The advantage of this design is that render calls can be reordered and
+// batched up to avoid OpenGL state changes as much as possible. This can
+// reduce the amount of OpenGL calls done in the system per frame by an order
+// of magnitude if assets are properly batched up into texture atlases.
+//
+// Rendering is simple: first everything fully opaque is rendered front to back
+// (so that no pixel is drawn twice). This allows for maximum program batching,
+// as for example all opaque rectangles can be rendered in one draw call,
+// ignoring z-value.
+//
+// In the second step, all drawing calls with (partially) transparent pixels
+// are done. This has to be done strictly in z ordering (back to front), so
+// that transparency works correctly. But common operations can still be
+// batched - for example the blitting of houses could all be done with the same
+// z value and using a common texture atlas. Then they could be drawn in one
+// woosh.
+//
+// Non overlapping rectangles can be drawn in parallel, ignoring z-order. I
+// experimented with a linear algorithm to find all overlapping rectangle
+// pairs (see bzr history), but it did not buy the performance I was hoping it
+// would. So I abandoned this idea again.
+//
+// Note: all draw calls that target a Texture are not going to the RenderQueue,
+// but are still immediately executed. The RenderQueue is only used for
+// rendering onto the screen.
+//
+// TODO(sirver): we could (even) better performance by being z-layer aware
+// while drawing. For example the UI could draw non-overlapping windows and
+// sibling children with the same z-value for better batching. Also for example
+// build-help symbols, buildings, and flags could all be drawn with the same
+// z-layer for better batching up. This would also get rid of the z-layer
+// issues we are having.
+class RenderQueue {
+public:
+	enum Program {
+		TERRAIN_BASE,
+		TERRAIN_DITHER,
+		TERRAIN_ROAD,
+		BLIT,
+		BLIT_MONOCHROME,
+		BLIT_BLENDED,
+		RECT,
+		LINE,
+		HIGHEST_PROGRAM_ID,
+	};
+
+	struct VanillaBlitArguments {
+		BlitSource texture;
+		float opacity;
+	};
+
+	struct MonochromeBlitArguments {
+		BlitSource texture;
+		RGBAColor blend;
+	};
+
+	struct BlendedBlitArguments {
+		BlitSource texture;
+		BlitSource mask;
+		RGBAColor blend;
+	};
+
+	struct RectArguments {
+		RGBAColor color;
+	};
+
+	struct LineArguments {
+		RGBColor color;
+		uint8_t line_width;
+	};
+
+	struct TerrainArguments {
+		TerrainArguments() {}
+
+		int gametime;
+		int renderbuffer_width;
+		int renderbuffer_height;
+		const DescriptionMaintainer<Widelands::TerrainDescription>* terrains;
+		FieldsToDraw* fields_to_draw;
+	};
+
+	// The union of all possible program arguments represents an Item that is
+	// enqueued in the Queue. This is on purpose not done with OOP so that the
+	// queue is more cache friendly.
+	struct Item {
+		Item() {}
+
+		inline bool operator<(const Item& other) const {
+			return key < other.key;
+		}
+
+		// The program that will be used to draw this item. Also defines which
+		// union type is filled in.
+		int program_id;
+
+		// The z-value in GL space that will be used for drawing.
+		float z_value;
+
+		// The bounding box in the renderbuffer where this draw will change pixels.
+		FloatRect destination_rect;
+
+		// The key for sorting this item in the queue. It depends on the type of
+		// item how this is calculated, but it will contain at least the program,
+		// the z-layer, if it is opaque or transparent and program specific
+		// options. After ordering the queue by this, it defines the batching.
+		uint64_t key;
+
+		// If this is opaque or, if not, which blend_mode to use.
+		BlendMode blend_mode;
+
+		union {
+			VanillaBlitArguments vanilla_blit_arguments;
+			MonochromeBlitArguments monochrome_blit_arguments;
+			BlendedBlitArguments blended_blit_arguments;
+			TerrainArguments terrain_arguments;
+			RectArguments rect_arguments;
+			LineArguments line_arguments;
+		};
+	};
+
+	static RenderQueue& instance();
+
+	// Enqueues 'item' in the queue with a higher 'z' value than the last enqueued item.
+	void enqueue(const Item& item);
+
+	// Draws all items in the queue in an optimal ordering and as much batching
+	// as possible. This will draw one complete frame onto the screen and this
+	// function is the only one that actually triggers draws to the screen
+	// directly.
+	void draw(int screen_width, int screen_height);
+
+private:
+	RenderQueue();
+
+	void draw_items(const std::vector<Item>& items);
+
+	// The z value that should be used for the next draw, so that it is on top
+	// of everything before.
+	int next_z_;
+
+	std::unique_ptr<TerrainProgram> terrain_program_;
+	std::unique_ptr<DitherProgram> dither_program_;
+	std::unique_ptr<RoadProgram> road_program_;
+
+	std::vector<Item> blended_items_;
+	std::vector<Item> opaque_items_;
+
+	DISALLOW_COPY_AND_ASSIGN(RenderQueue);
+};
+
+
+#endif  // end of include guard: WL_GRAPHIC_RENDER_QUEUE_H

=== modified file 'src/graphic/rendertarget.cc'
--- src/graphic/rendertarget.cc	2014-12-07 20:13:27 +0000
+++ src/graphic/rendertarget.cc	2015-02-21 07:13:48 +0000
@@ -97,7 +97,7 @@
 			*prevofs = m_offset;
 
 		// Apply the changes
-		m_offset = rc.top_left() - (newrect.top_left() - m_rect.top_left() - m_offset);
+		m_offset = rc.origin() - (newrect.origin() - m_rect.origin() - m_offset);
 		m_rect = newrect;
 
 		return true;
@@ -123,17 +123,14 @@
 /**
  * This functions draws a line in the target
  */
-void RenderTarget::draw_line
-	(int32_t x1, int32_t y1, int32_t x2, int32_t y2,
-	 const RGBColor& color, uint8_t gwidth)
-{
-	::draw_line(x1 + m_offset.x + m_rect.x,
-	            y1 + m_offset.y + m_rect.y,
-	            x2 + m_offset.x + m_rect.x,
-	            y2 + m_offset.y + m_rect.y,
-	            color,
-	            gwidth,
-	            m_surface);
+void RenderTarget::draw_line(const Point& start,
+                             const Point& end,
+                             const RGBColor& color,
+                             uint8_t line_width) {
+	m_surface->draw_line(Point(start.x + m_offset.x + m_rect.x, start.y + m_offset.y + m_rect.y),
+	                     Point(end.x + m_offset.x + m_rect.x, end.y + m_offset.y + m_rect.y),
+	                     color,
+	                     line_width);
 }
 
 /**
@@ -151,14 +148,14 @@
 {
 	Rect r(rect);
 	if (clip(r))
-		::fill_rect(r, clr, m_surface);
+		m_surface->fill_rect(r, clr);
 }
 
 void RenderTarget::brighten_rect(const Rect& rect, int32_t factor)
 {
 	Rect r(rect);
 	if (clip(r))
-		::brighten_rect(r, factor, m_surface);
+		m_surface->brighten_rect(r, factor);
 }
 
 /**
@@ -175,12 +172,11 @@
 	Rect srcrc(Point(0, 0), image->width(), image->height());
 
 	if (to_surface_geometry(&destination_point, &srcrc)) {
-		::blit(Rect(destination_point.x, destination_point.y, srcrc.w, srcrc.h),
-		     *image,
-		     srcrc,
-		     1.,
-		     blend_mode,
-		     m_surface);
+		m_surface->blit(Rect(destination_point.x, destination_point.y, srcrc.w, srcrc.h),
+		                *image,
+		                srcrc,
+		                1.,
+		                blend_mode);
 	}
 }
 
@@ -202,12 +198,11 @@
 
 	Point destination_point(dst);
 	if (to_surface_geometry(&destination_point, &srcrc))
-		::blit(Rect(destination_point.x, destination_point.y, srcrc.w, srcrc.h),
-		       *image,
-		       srcrc,
-		       1.,
-		       blend_mode,
-		       m_surface);
+		m_surface->blit(Rect(destination_point.x, destination_point.y, srcrc.w, srcrc.h),
+		         *image,
+		         srcrc,
+		         1.,
+		         blend_mode);
 }
 
 void RenderTarget::blitrect_scale(const Rect& dst,
@@ -219,12 +214,11 @@
 	Point destination_point(dst.x, dst.y);
 	Rect srcrect(source_rect);
 	if (to_surface_geometry(&destination_point, &srcrect)) {
-		::blit(Rect(destination_point.x, destination_point.y, dst.w, dst.h),
-		       *image,
-		       source_rect,
-		       opacity,
-		       blend_mode,
-		       m_surface);
+		m_surface->blit(Rect(destination_point.x, destination_point.y, dst.w, dst.h),
+		                *image,
+		                source_rect,
+		                opacity,
+		                blend_mode);
 	}
 }
 
@@ -235,12 +229,11 @@
 	Point destination_point(destination_rect.x, destination_rect.y);
 	Rect srcrect(source_rect);
 	if (to_surface_geometry(&destination_point, &srcrect)) {
-		blit_monochrome(
+		m_surface->blit_monochrome(
 		   Rect(destination_point.x, destination_point.y, destination_rect.w, destination_rect.h),
 		   *image,
 		   source_rect,
-		   blend,
-		   m_surface);
+		   blend);
 	}
 }
 
@@ -297,7 +290,7 @@
 					srcrc.w = r.w - tx;
 
 				const Rect dst_rect(r.x + tx, r.y + ty, srcrc.w, srcrc.h);
-				::blit(dst_rect, *image, srcrc, 1., blend_mode, m_surface);
+				m_surface->blit(dst_rect, *image, srcrc, 1., blend_mode);
 
 				tx += srcrc.w;
 
@@ -350,7 +343,7 @@
 	const Animation& anim = g_gr->animations().get_animation(animation);
 
 	Point destination_point = dst - anim.hotspot();
-	destination_point += gsrcrc.top_left();
+	destination_point += gsrcrc.origin();
 
 	Rect srcrc(gsrcrc);
 
@@ -455,6 +448,6 @@
 		srcrc->h = m_rect.h - dst->y;
 	}
 
-	*dst += m_rect.top_left();
+	*dst += m_rect.origin();
 	return true;
 }

=== modified file 'src/graphic/rendertarget.h'
--- src/graphic/rendertarget.h	2014-12-06 15:32:50 +0000
+++ src/graphic/rendertarget.h	2015-02-21 07:13:48 +0000
@@ -60,8 +60,7 @@
 	int32_t width() const;
 	int32_t height() const;
 
-	void draw_line
-		(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const RGBColor& color, uint8_t width = 1);
+	void draw_line(const Point& start, const Point& end, const RGBColor& color, uint8_t width = 1);
 	void draw_rect(const Rect&, const RGBColor&);
 	void fill_rect(const Rect&, const RGBAColor&);
 	void brighten_rect(const Rect&, int32_t factor);

=== modified file 'src/graphic/richtext.cc'
--- src/graphic/richtext.cc	2014-12-06 12:22:35 +0000
+++ src/graphic/richtext.cc	2015-02-21 07:13:48 +0000
@@ -482,7 +482,7 @@
 	{
 		Rect oldbox;
 		Point oldofs;
-		Rect bbox((*elt)->bbox.top_left() + offset, (*elt)->bbox.w, (*elt)->bbox.h);
+		Rect bbox((*elt)->bbox.origin() + offset, (*elt)->bbox.w, (*elt)->bbox.h);
 
 		if (dst.enter_window(bbox, &oldbox, &oldofs)) {
 			if (background)

=== modified file 'src/graphic/screen.cc'
--- src/graphic/screen.cc	2014-12-27 09:59:12 +0000
+++ src/graphic/screen.cc	2015-02-21 07:13:48 +0000
@@ -24,20 +24,12 @@
 
 #include "base/wexception.h"
 #include "graphic/gl/utils.h"
+#include "graphic/render_queue.h"
 #include "graphic/texture.h"
 
 Screen::Screen(int w, int h) : m_w(w), m_h(h) {
 }
 
-void Screen::pixel_to_gl(float* x, float* y) const {
-	*x = (*x / m_w) * 2. - 1.;
-	*y = 1. - (*y / m_h) * 2.;
-}
-
-void Screen::setup_gl() {
-	glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
 int Screen::width() const {
 	return m_w;
 }
@@ -50,17 +42,7 @@
 	std::unique_ptr<uint8_t[]> pixels(new uint8_t[m_w * m_h * 4]);
 	glReadPixels(0, 0, m_w, m_h, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
 
-	// Swap order of rows in m_pixels, to compensate for the upside-down nature of the
-	// OpenGL coordinate system.
-	uint8_t* begin_row = pixels.get();
-	uint8_t* end_row = pixels.get() + (m_w * (m_h - 1) * 4);
-	while (begin_row < end_row) {
-		for (int x = 0; x < m_w * 4; ++x) {
-			std::swap(begin_row[x], end_row[x]);
-		}
-		begin_row += m_w * 4;
-		end_row -= m_w * 4;
-	}
+	Gl::swap_rows(m_w, m_h, m_w * 4, 4, pixels.get());
 
 	// Ownership of pixels is not taken here. But the Texture() transfers it to
 	// the GPU, frees the SDL surface and after that we are free to free
@@ -77,3 +59,64 @@
 
 	return std::unique_ptr<Texture>(new Texture(surface));
 }
+
+void Screen::do_blit(const FloatRect& dst_rect,
+                     const BlitSource& texture,
+                     float opacity,
+                     BlendMode blend_mode) {
+	RenderQueue::Item i;
+	i.program_id = RenderQueue::Program::BLIT;
+	i.blend_mode = blend_mode;
+	i.destination_rect = dst_rect;
+	i.vanilla_blit_arguments.texture = texture;
+	i.vanilla_blit_arguments.opacity = opacity;
+	RenderQueue::instance().enqueue(i);
+}
+
+void Screen::do_blit_blended(const FloatRect& dst_rect,
+                             const BlitSource& texture,
+                             const BlitSource& mask,
+                             const RGBColor& blend) {
+	RenderQueue::Item i;
+	i.destination_rect = dst_rect;
+	i.program_id = RenderQueue::Program::BLIT_BLENDED;
+	i.blend_mode = BlendMode::UseAlpha;
+	i.blended_blit_arguments.texture = texture;
+	i.blended_blit_arguments.mask = mask;
+	i.blended_blit_arguments.blend = blend;
+	RenderQueue::instance().enqueue(i);
+}
+
+void Screen::do_blit_monochrome(const FloatRect& dst_rect,
+                                const BlitSource& texture,
+                                const RGBAColor& blend) {
+	RenderQueue::Item i;
+	i.program_id = RenderQueue::Program::BLIT_MONOCHROME;
+	i.blend_mode = BlendMode::UseAlpha;
+	i.destination_rect = dst_rect;
+	i.monochrome_blit_arguments.texture = texture;
+	i.monochrome_blit_arguments.blend = blend;
+	RenderQueue::instance().enqueue(i);
+}
+
+void Screen::do_draw_line(const FloatPoint& start,
+                          const FloatPoint& end,
+                          const RGBColor& color,
+								  const int line_width) {
+	RenderQueue::Item i;
+	i.program_id = RenderQueue::Program::LINE;
+	i.blend_mode = BlendMode::Copy;
+	i.destination_rect = FloatRect(start.x, start.y, end.x - start.x, end.y - start.y);
+	i.line_arguments.color = color;
+	i.line_arguments.line_width = line_width;
+	RenderQueue::instance().enqueue(i);
+}
+
+void Screen::do_fill_rect(const FloatRect& dst_rect, const RGBAColor& color, BlendMode blend_mode) {
+	RenderQueue::Item i;
+	i.program_id = RenderQueue::Program::RECT;
+	i.blend_mode = blend_mode;
+	i.destination_rect = dst_rect;
+	i.rect_arguments.color = color;
+	RenderQueue::instance().enqueue(i);
+}

=== modified file 'src/graphic/screen.h'
--- src/graphic/screen.h	2014-12-07 21:34:11 +0000
+++ src/graphic/screen.h	2015-02-21 07:13:48 +0000
@@ -35,8 +35,6 @@
 	// Implements Surface.
 	int width() const override;
 	int height() const override;
-	void setup_gl() override;
-	void pixel_to_gl(float* x, float* y) const override;
 
 	// Reads out the current pixels in the framebuffer and returns
 	// them as a texture for screenshots. This is a very slow process,
@@ -44,6 +42,24 @@
 	std::unique_ptr<Texture> to_texture() const;
 
 private:
+	void do_blit(const FloatRect& dst_rect,
+	             const BlitSource& texture,
+	             float opacity,
+	             BlendMode blend_mode) override;
+	void do_blit_blended(const FloatRect& dst_rect,
+	                     const BlitSource& texture,
+	                     const BlitSource& mask,
+	                     const RGBColor& blend) override;
+	void do_blit_monochrome(const FloatRect& dst_rect,
+	                        const BlitSource& texture,
+	                        const RGBAColor& blend) override;
+	void do_draw_line(const FloatPoint& start,
+	                  const FloatPoint& end,
+	                  const RGBColor& color,
+	                  int width) override;
+	void
+	do_fill_rect(const FloatRect& dst_rect, const RGBAColor& color, BlendMode blend_mode) override;
+
 	const int m_w, m_h;
 
 	DISALLOW_COPY_AND_ASSIGN(Screen);

=== modified file 'src/graphic/surface.cc'
--- src/graphic/surface.cc	2015-01-15 07:14:53 +0000
+++ src/graphic/surface.cc	2015-02-21 07:13:48 +0000
@@ -26,197 +26,100 @@
 #include <SDL.h>
 
 #include "base/macros.h"
-#include "graphic/gl/blit_program.h"
-#include "graphic/gl/draw_line_program.h"
-#include "graphic/gl/fill_rect_program.h"
+#include "graphic/gl/coordinate_conversion.h"
 #include "graphic/gl/utils.h"
-#include "graphic/graphic.h"
-#include "graphic/texture.h"
-
 
 namespace  {
 
-// Convert the 'rect' in pixel space into opengl space.
-enum class ConversionMode {
-	// Convert the rect as given.
-	kExact,
-
-	// Convert the rect so that the borders are in the center
-	// of the pixels.
-	kMidPoint,
-};
-
-FloatRect to_opengl(const Surface& surface, const Rect& rect, ConversionMode mode) {
-	const float delta = mode == ConversionMode::kExact ? 0. : 0.5;
-	float x1 = rect.x + delta;
-	float y1 = rect.y + delta;
-	surface.pixel_to_gl(&x1, &y1);
-	float x2 = rect.x + rect.w - delta;
-	float y2 = rect.y + rect.h - delta;
-	surface.pixel_to_gl(&x2, &y2);
-	return FloatRect(x1, y1, x2 - x1, y2 - y1);
-}
-
-// Converts the pixel (x, y) in a texture to a gl coordinate in [0, 1].
-inline void pixel_to_gl_texture(const int width, const int height, float* x, float* y) {
-	*x = (*x / width);
-	*y = (*y / height);
-}
-
-// Convert 'dst' and 'srcrc' from pixel space into opengl space, taking into
-// account that we might be a subtexture in a bigger texture.
-void src_and_dst_rect_to_gl(const Surface& surface,
-                            const Image& image,
-                            const Rect& dst_rect,
-                            const Rect& src_rect,
-                            FloatRect* gl_dst_rect,
-                            FloatRect* gl_src_rect) {
+// Convert 'srcrc' from pixel space into opengl space, taking into account that
+// it might be a subtexture in a bigger texture.
+BlitSource to_blit_source(const Image& image, const Rect& src_rect) {
 	// Source Rectangle. We have to take into account that the texture might be
 	// a subtexture in another bigger texture. So we first figure out the pixel
 	// coordinates given it is a full texture (values between 0 and 1) and then
 	// adjust these for the texture coordinates in the parent texture.
+
+	const FloatRect rc(src_rect.x, src_rect.y, src_rect.w, src_rect.h);
+	const FloatRect in_texture = rect_to_gl_texture(image.width(), image.height(), rc);
 	const FloatRect& texture_coordinates = image.texture_coordinates();
-
-	float x1 = src_rect.x;
-	float y1 = src_rect.y;
-	pixel_to_gl_texture(image.width(), image.height(), &x1, &y1);
-	x1 = texture_coordinates.x + x1 * texture_coordinates.w;
-	y1 = texture_coordinates.y + y1 * texture_coordinates.h;
-
-	float x2 = src_rect.x + src_rect.w;
-	float y2 = src_rect.y + src_rect.h;
-	pixel_to_gl_texture(image.width(), image.height(), &x2, &y2);
-	x2 = texture_coordinates.x + x2 * texture_coordinates.w;
-	y2 = texture_coordinates.y + y2 * texture_coordinates.h;
-
-	gl_src_rect->x = x1;
-	gl_src_rect->y = y1;
-	gl_src_rect->w = x2 - x1;
-	gl_src_rect->h = y2 - y1;
-
-	*gl_dst_rect = to_opengl(surface, dst_rect, ConversionMode::kExact);
+	const float left = texture_coordinates.x + in_texture.x * texture_coordinates.w;
+	const float bottom = texture_coordinates.y + in_texture.y * texture_coordinates.h;
+	const float right = texture_coordinates.x + (in_texture.x + in_texture.w) * texture_coordinates.w;
+	const float top = texture_coordinates.y + (in_texture.y + in_texture.h) * texture_coordinates.h;
+
+	return BlitSource{
+	   FloatRect(left, bottom, right - left, top - bottom), image.get_gl_texture(),
+	};
 }
 
 }  // namespace
 
-
-void fill_rect(const Rect& rc, const RGBAColor& clr, Surface* surface) {
-	surface->setup_gl();
-	glViewport(0, 0, surface->width(), surface->height());
-
-	glBlendFunc(GL_ONE, GL_ZERO);
-
-	FillRectProgram::instance().draw(to_opengl(*surface, rc, ConversionMode::kExact), clr);
-
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-}
-
-void brighten_rect(const Rect& rc, const int32_t factor, Surface * surface)
+void draw_rect(const Rect& rc, const RGBColor& clr, Surface* surface) {
+	surface->draw_line(Point(rc.x, rc.y), Point(rc.x + rc.w, rc.y), clr, 1);
+	surface->draw_line(Point(rc.x + rc.w, rc.y), Point(rc.x + rc.w, rc.y + rc.h), clr, 1);
+	surface->draw_line(Point(rc.x + rc.w, rc.y + rc.h), Point(rc.x, rc.y + rc.h), clr, 1);
+	surface->draw_line(Point(rc.x, rc.y + rc.h), Point(rc.x, rc.y), clr, 1);
+}
+
+void Surface::fill_rect(const Rect& rc, const RGBAColor& clr) {
+	const FloatRect rect = rect_to_gl_renderbuffer(width(), height(), rc);
+	do_fill_rect(rect, clr, BlendMode::Copy);
+}
+
+void Surface::brighten_rect(const Rect& rc, const int32_t factor)
 {
-	if (!factor)
+	if (!factor) {
 		return;
-
-	surface->setup_gl();
-	glViewport(0, 0, surface->width(), surface->height());
-
-	// The simple trick here is to fill the rect, but using a different glBlendFunc that will sum
-	// src and target (or subtract them if factor is negative).
-	if (factor < 0) {
-		glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
-	}
-
-	glBlendFunc(GL_ONE, GL_ONE);
-
-	const int delta = std::abs(factor);
-	FillRectProgram::instance().draw(
-	   to_opengl(*surface, rc, ConversionMode::kExact), RGBAColor(delta, delta, delta, 0));
-
-	if (factor < 0) {
-		glBlendEquation(GL_FUNC_ADD);
-	}
-
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	}
+
+	const BlendMode blend_mode = factor < 0 ? BlendMode::Subtract : BlendMode::UseAlpha;
+	const int abs_factor = std::abs(factor);
+	const RGBAColor color(abs_factor, abs_factor, abs_factor, 0);
+	const FloatRect rect = rect_to_gl_renderbuffer(width(), height(), rc);
+	do_fill_rect(rect, color, blend_mode);
 }
 
-void draw_line
-	(int x1, int y1, int x2, int y2, const RGBColor& color, int gwidth, Surface * surface)
+void Surface::draw_line
+	(const Point& start, const Point& end, const RGBColor& color, int line_width)
 {
-	surface->setup_gl();
-	glViewport(0, 0, surface->width(), surface->height());
-
-	float gl_x1 = x1 + 0.5;
-	float gl_y1 = y1 + 0.5;
-	surface->pixel_to_gl(&gl_x1, &gl_y1);
-
-	float gl_x2 = x2 + 0.5;
-	float gl_y2 = y2 + 0.5;
-	surface->pixel_to_gl(&gl_x2, &gl_y2);
-
-	DrawLineProgram::instance().draw(gl_x1, gl_y1, gl_x2, gl_y2, color, gwidth);
-}
-
-void blit_monochrome(const Rect& dst_rect,
-                     const Image& image,
-                     const Rect& src_rect,
-                     const RGBAColor& blend,
-                     Surface* surface) {
-	surface->setup_gl();
-	glViewport(0, 0, surface->width(), surface->height());
-
-	FloatRect gl_dst_rect, gl_src_rect;
-	src_and_dst_rect_to_gl(*surface, image, dst_rect, src_rect, &gl_dst_rect, &gl_src_rect);
-
-	MonochromeBlitProgram::instance().draw(
-	   gl_dst_rect, gl_src_rect, image.get_gl_texture(), blend);
-
-	// TODO(sirver): This is a hacky attempt to fix 1409267. It
-	// should not stick around.
-	glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-void blit_blended(const Rect& dst_rect,
-                  const Image& image,
-                  const Image& mask,
-                  const Rect& src_rect,
-                  const RGBColor& blend,
-                  Surface* surface) {
-	surface->setup_gl();
-	glViewport(0, 0, surface->width(), surface->height());
-
-	FloatRect gl_dst_rect, gl_src_rect;
-	src_and_dst_rect_to_gl(*surface, image, dst_rect, src_rect, &gl_dst_rect, &gl_src_rect);
-
-	BlendedBlitProgram::instance().draw(
-	   gl_dst_rect, gl_src_rect, image.get_gl_texture(), mask.get_gl_texture(), blend);
-
-	// TODO(sirver): This is a hacky attempt to fix 1409267. It
-	// should not stick around.
-	glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-void draw_rect(const Rect& rc, const RGBColor& clr, Surface* surface) {
-	draw_line(rc.x, rc.y, rc.x + rc.w, rc.y, clr, 1, surface);
-	draw_line(rc.x + rc.w, rc.y, rc.x + rc.w, rc.y + rc.h, clr, 1, surface);
-	draw_line(rc.x + rc.w, rc.y + rc.h, rc.x, rc.y + rc.h, clr, 1, surface);
-	draw_line(rc.x, rc.y + rc.h, rc.x, rc.y, clr, 1, surface);
-}
-
-void blit(const Rect& dst_rect,
-          const Image& image,
-          const Rect& src_rect,
-          float opacity,
-          BlendMode blend_mode,
-          Surface* surface) {
-	glViewport(0, 0, surface->width(), surface->height());
-	surface->setup_gl();
-
-	FloatRect gl_dst_rect, gl_src_rect;
-	src_and_dst_rect_to_gl(*surface, image, dst_rect, src_rect, &gl_dst_rect, &gl_src_rect);
-
-	VanillaBlitProgram::instance().draw(
-	   gl_dst_rect, gl_src_rect, image.get_gl_texture(), opacity, blend_mode);
-
-	// TODO(sirver): This is a hacky attempt to fix 1409267. It
-	// should not stick around.
-	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	float gl_x1 = start.x;
+	float gl_y1 = start.y;
+
+	// Include the end pixel.
+	float gl_x2 = end.x + 1.;
+	float gl_y2 = end.y + 1.;
+	pixel_to_gl_renderbuffer(width(), height(), &gl_x1, &gl_y1);
+	pixel_to_gl_renderbuffer(width(), height(), &gl_x2, &gl_y2);
+
+	do_draw_line(FloatPoint(gl_x1, gl_y1), FloatPoint(gl_x2, gl_y2), color, line_width);
+}
+
+void Surface::blit_monochrome(const Rect& dst_rect,
+                              const Image& image,
+                              const Rect& src_rect,
+                              const RGBAColor& blend) {
+	const BlitSource texture = to_blit_source(image, src_rect);
+	const FloatRect rect = rect_to_gl_renderbuffer(width(), height(), dst_rect);
+	do_blit_monochrome(rect, texture, blend);
+}
+
+void Surface::blit_blended(const Rect& dst_rect,
+                           const Image& image,
+                           const Image& texture_mask,
+                           const Rect& src_rect,
+                           const RGBColor& blend) {
+	const BlitSource texture = to_blit_source(image, src_rect);
+	const BlitSource mask = to_blit_source(texture_mask, src_rect);
+	const FloatRect rect = rect_to_gl_renderbuffer(width(), height(), dst_rect);
+	do_blit_blended(rect, texture, mask, blend);
+}
+
+void Surface::blit(const Rect& dst_rect,
+                   const Image& image,
+                   const Rect& src_rect,
+                   float opacity,
+                   BlendMode blend_mode) {
+	const BlitSource texture = to_blit_source(image, src_rect);
+	const FloatRect rect = rect_to_gl_renderbuffer(width(), height(), dst_rect);
+	do_blit(rect, texture, opacity, blend_mode);
 }

=== modified file 'src/graphic/surface.h'
--- src/graphic/surface.h	2014-12-07 21:34:11 +0000
+++ src/graphic/surface.h	2015-02-21 07:13:48 +0000
@@ -26,6 +26,7 @@
 #include "base/rect.h"
 #include "graphic/blend_mode.h"
 #include "graphic/color.h"
+#include "graphic/gl/blit_source.h"
 #include "graphic/image.h"
 
 class Texture;
@@ -41,47 +42,60 @@
 	virtual int width() const = 0;
 	virtual int height() const = 0;
 
-	// Converts the given pixel into an OpenGl point. This might
-	// need some flipping of axis, depending if you want to render
-	// on the screen or not.
-	virtual void pixel_to_gl(float* x, float* y) const = 0;
-
-	// Setups OpenGL to render to this surface.
-	virtual void setup_gl() = 0;
+	/// This draws a part of 'texture'.
+	void blit(const Rect& dst,
+	          const Image&,
+	          const Rect& srcrc,
+	          const float opacity,
+	          BlendMode blend_mode);
+
+	/// This draws a playercolor blended image. See BlendedBlitProgram.
+	void blit_blended(const Rect& dst,
+	                  const Image& image,
+	                  const Image& texture_mask,
+	                  const Rect& srcrc,
+	                  const RGBColor& blend);
+
+	/// This draws a grayed out version. See MonochromeBlitProgram.
+	void
+	blit_monochrome(const Rect& dst, const Image&, const Rect& srcrc, const RGBAColor& multiplier);
+
+	/// Draws a filled rect to the destination. No blending takes place, the values
+	// in the target are just replaced (i.e. / BlendMode would be BlendMode::Copy).
+	void fill_rect(const Rect&, const RGBAColor&);
+
+	/// draw a line to the destination
+	void draw_line(const Point& start, const Point& end, const RGBColor& color, int width);
+
+	/// makes a rectangle on the destination brighter (or darker).
+	void brighten_rect(const Rect&, int factor);
 
 private:
+	/// The actual implementation of the methods below.
+	virtual void do_blit(const FloatRect& dst_rect,
+	                     const BlitSource& texture,
+	                     float opacity,
+	                     BlendMode blend_mode) = 0;
+
+	virtual void do_blit_blended(const FloatRect& dst_rect,
+	                             const BlitSource& texture,
+	                             const BlitSource& mask,
+	                             const RGBColor& blend) = 0;
+
+	virtual void do_blit_monochrome(const FloatRect& dst_rect,
+	                                const BlitSource& texture,
+	                                const RGBAColor& blend) = 0;
+
+	virtual void
+	do_draw_line(const FloatPoint& start, const FloatPoint& end, const RGBColor& color, int width) = 0;
+
+	virtual void
+	do_fill_rect(const FloatRect& dst_rect, const RGBAColor& color, BlendMode blend_mode) = 0;
+
 	DISALLOW_COPY_AND_ASSIGN(Surface);
 };
 
 /// Draws a rect (frame only) to the surface.
 void draw_rect(const Rect&, const RGBColor&, Surface* destination);
 
-/// This draws a part of 'texture' to 'surface'.
-void blit
-	(const Rect& dst, const Image&, const Rect& srcrc, const float opacity,
-	 BlendMode blend_mode, Surface* destination);
-
-/// This draws a grayed out version. See MonochromeBlitProgram.
-void
-blit_monochrome
-	(const Rect& dst, const Image&, const Rect& srcrc,
-	 const RGBAColor& multiplier, Surface* destination);
-
-/// This draws a playercolor blended image. See BlendedBlitProgram.
-void blit_blended
-	(const Rect& dst, const Image& image, const Image& mask, const Rect&
-	 srcrc, const RGBColor& blend, Surface* destination);
-
-/// Draws a filled rect to the destination. No blending takes place, the values
-// in the target are just replaced (i.e. / BlendMode would be BlendMode::Copy).
-void fill_rect(const Rect&, const RGBAColor&, Surface* destination);
-
-/// draw a line to the destination
-void draw_line
-	(int x1, int y1, int x2, int y2, const RGBColor& color,
-	 int width, Surface* destination);
-
-/// makes a rectangle on the destination brighter (or darker).
-void brighten_rect(const Rect&, int factor, Surface* destination);
-
 #endif  // end of include guard: WL_GRAPHIC_SURFACE_H

=== modified file 'src/graphic/text/rt_render.cc'
--- src/graphic/text/rt_render.cc	2014-12-27 09:59:12 +0000
+++ src/graphic/text/rt_render.cc	2015-02-21 07:13:48 +0000
@@ -332,15 +332,15 @@
 uint16_t TextNode::hotspot_y() {
 	return m_font.ascent(m_s.font_style);
 }
+
 Texture* TextNode::render(TextureCache* texture_cache) {
 	const Texture& img = m_font.render(m_txt, m_s.font_color, m_s.font_style, texture_cache);
 	Texture* rv = new Texture(img.width(), img.height());
-	blit(Rect(0, 0, img.width(), img.height()),
-	     img,
-	     Rect(0, 0, img.width(), img.height()),
-	     1.,
-	     BlendMode::Copy,
-	     rv);
+	rv->blit(Rect(0, 0, img.width(), img.height()),
+	         img,
+	         Rect(0, 0, img.width(), img.height()),
+	         1.,
+	         BlendMode::Copy);
 	return rv;
 }
 
@@ -368,7 +368,7 @@
 	Texture* rv = new Texture(m_w, m_h);
 	for (uint16_t curx = 0; curx < m_w; curx += t.width()) {
 		Rect srcrect(Point(0, 0), min<int>(t.width(), m_w - curx), m_h);
-		blit(Rect(curx, 0, srcrect.w, srcrect.h), t, srcrect, 1., BlendMode::Copy, rv);
+		rv->blit(Rect(curx, 0, srcrect.w, srcrect.h), t, srcrect, 1., BlendMode::Copy);
 	}
 	return rv;
 }
@@ -385,7 +385,7 @@
 	Texture* render(TextureCache* texture_cache) override {
 		if (m_show_spaces) {
 			Texture* rv = new Texture(m_w, m_h);
-			fill_rect(Rect(0, 0, m_w, m_h), RGBAColor(0xff, 0, 0, 0xff), rv);
+			rv->fill_rect(Rect(0, 0, m_w, m_h), RGBAColor(0xff, 0, 0, 0xff));
 			return rv;
 		}
 		return TextNode::render(texture_cache);
@@ -437,10 +437,10 @@
 				dst.y = 0;
 				srcrect.w = dst.w = min<int>(m_bg->width(), m_w - curx);
 				srcrect.h = dst.h = m_h;
-				blit(dst, *m_bg, srcrect, 1., BlendMode::Copy, rv);
+				rv->blit(dst, *m_bg, srcrect, 1., BlendMode::Copy);
 			}
 		} else {
-			fill_rect(Rect(0, 0, m_w, m_h), RGBAColor(255, 255, 255, 0), rv);
+			rv->fill_rect(Rect(0, 0, m_w, m_h), RGBAColor(255, 255, 255, 0));
 		}
 		return rv;
 	}
@@ -477,12 +477,12 @@
 	uint16_t hotspot_y() override {return height();}
 	Texture* render(TextureCache* texture_cache) override {
 		Texture* rv = new Texture(width(), height());
-		fill_rect(Rect(0, 0, rv->width(), rv->height()), RGBAColor(255, 255, 255, 0), rv);
+		rv->fill_rect(Rect(0, 0, rv->width(), rv->height()), RGBAColor(255, 255, 255, 0));
 
 		// Draw Solid background Color
 		bool set_alpha = true;
 		if (m_bg_clr_set) {
-			fill_rect(Rect(Point(m_margin.left, m_margin.top), m_w, m_h), m_bg_clr, rv);
+			rv->fill_rect(Rect(Point(m_margin.left, m_margin.top), m_w, m_h), m_bg_clr);
 			set_alpha = false;
 		}
 
@@ -497,7 +497,7 @@
 					dst.y = cury;
 					src.w = dst.w = min<int>(m_bg_img->width(), m_w + m_margin.left - curx);
 					src.h = dst.h = min<int>(m_bg_img->height(), m_h + m_margin.top - cury);
-					blit(dst, *m_bg_img, src, 1., BlendMode::Copy, rv);
+					rv->blit(dst, *m_bg_img, src, 1., BlendMode::Copy);
 				}
 			}
 			set_alpha = false;
@@ -512,7 +512,7 @@
 				                node_texture->height());
 				Rect src = Rect(0, 0, node_texture->width(), node_texture->height());
 
-				blit(dst, *node_texture, src, 1., set_alpha ? BlendMode::Copy : BlendMode::UseAlpha, rv);
+				rv->blit(dst, *node_texture, src, 1., set_alpha ? BlendMode::Copy : BlendMode::UseAlpha);
 				delete node_texture;
 			}
 			delete n;
@@ -563,11 +563,11 @@
 
 Texture* ImgRenderNode::render(TextureCache* /* texture_cache */) {
 	Texture* rv = new Texture(m_image.width(), m_image.height());
-	blit(Rect(0, 0, m_image.width(), m_image.height()),
+	rv->blit(Rect(0, 0, m_image.width(), m_image.height()),
 	         m_image,
 	         Rect(0, 0, m_image.width(), m_image.height()),
 				1.,
-	         BlendMode::Copy, rv);
+	         BlendMode::Copy);
 	return rv;
 }
 // End: Helper Stuff

=== modified file 'src/graphic/texture.cc'
--- src/graphic/texture.cc	2014-12-27 09:59:12 +0000
+++ src/graphic/texture.cc	2015-02-21 07:13:48 +0000
@@ -20,10 +20,15 @@
 
 #include <cassert>
 
+#include <SDL.h>
+
 #include "base/log.h"
 #include "base/macros.h"
 #include "base/wexception.h"
 #include "graphic/gl/blit_program.h"
+#include "graphic/gl/coordinate_conversion.h"
+#include "graphic/gl/draw_line_program.h"
+#include "graphic/gl/fill_rect_program.h"
 #include "graphic/gl/utils.h"
 #include "graphic/graphic.h"
 #include "graphic/sdl_utils.h"
@@ -31,6 +36,36 @@
 
 namespace  {
 
+namespace  {
+
+/**
+ * \return the standard 32-bit RGBA format that we use for our textures.
+ */
+const SDL_PixelFormat & rgba_format()
+{
+	static SDL_PixelFormat format;
+	static bool init = false;
+	if (init)
+		return format;
+
+	init = true;
+	memset(&format, 0, sizeof(format));
+	format.BitsPerPixel = 32;
+	format.BytesPerPixel = 4;
+	format.Rmask = 0x000000ff;
+	format.Gmask = 0x0000ff00;
+	format.Bmask = 0x00ff0000;
+	format.Amask = 0xff000000;
+	format.Rshift = 0;
+	format.Gshift = 8;
+	format.Bshift = 16;
+	format.Ashift = 24;
+	return format;
+}
+
+}  // namespace
+
+
 class GlFramebuffer {
 public:
 	static GlFramebuffer& instance() {
@@ -105,6 +140,8 @@
 
 	SDL_LockSurface(surface);
 
+	Gl::swap_rows(m_w, m_h, surface->pitch, bpp, static_cast<uint8_t*>(surface->pixels));
+
 	glTexImage2D
         (GL_TEXTURE_2D, 0, static_cast<GLint>(intensity ? GL_INTENSITY : GL_RGBA), m_w, m_h, 0,
 		 pixels_format, GL_UNSIGNED_BYTE, surface->pixels);
@@ -124,10 +161,8 @@
 	m_texture = texture;
 	m_owns_texture = false;
 
-	m_texture_coordinates.w = static_cast<float>(m_w - 1) / parent_w;
-	m_texture_coordinates.h = static_cast<float>(m_h - 1) / parent_h;
-	m_texture_coordinates.x = (static_cast<float>(subrect.x) + 0.5) / parent_w;
-	m_texture_coordinates.y = (static_cast<float>(subrect.y) + 0.5) / parent_h;
+	m_texture_coordinates =
+	   rect_to_gl_texture(parent_w, parent_h, FloatRect(subrect.x, subrect.y, subrect.w, subrect.h));
 }
 
 Texture::~Texture()
@@ -153,11 +188,6 @@
 	return m_texture_coordinates;
 }
 
-void Texture::pixel_to_gl(float* x, float* y) const {
-	*x = (*x / m_w) * 2. - 1.;
-	*y = (*y / m_h) * 2. - 1.;
-}
-
 void Texture::init(uint16_t w, uint16_t h)
 {
 	m_w = w;
@@ -218,40 +248,71 @@
 	m_pixels.reset(nullptr);
 }
 
-uint8_t * Texture::get_pixels() const
-{
-	return m_pixels.get();
-}
-
-uint32_t Texture::get_pixel(uint16_t x, uint16_t y) {
-	assert(m_pixels);
-	assert(x < m_w);
-	assert(y < m_h);
-
-	uint8_t * data = &m_pixels[y * get_pitch() + 4 * x];
-	return *(reinterpret_cast<uint32_t *>(data));
-}
-
-uint16_t Texture::get_pitch() const {
-	return 4 * m_w;
-}
-
-const SDL_PixelFormat & Texture::format() const {
-	return Gl::gl_rgba_format();
-}
-
-
-void Texture::set_pixel(uint16_t x, uint16_t y, uint32_t clr) {
-	assert(m_pixels);
-	assert(x < m_w);
-	assert(y < m_h);
-
-	uint8_t * data = &m_pixels[y * get_pitch() + 4 * x];
-	*(reinterpret_cast<uint32_t *>(data)) = clr;
+RGBAColor Texture::get_pixel(uint16_t x, uint16_t y) {
+	assert(m_pixels);
+	assert(x < m_w);
+	assert(y < m_h);
+
+	RGBAColor color;
+
+	SDL_GetRGBA(*reinterpret_cast<uint32_t*>(&m_pixels[(m_h - y - 1) * 4 * m_w + 4 * x]),
+	            &rgba_format(),
+	            &color.r,
+	            &color.g,
+	            &color.b,
+	            &color.a);
+	return color;
+}
+
+void Texture::set_pixel(uint16_t x, uint16_t y, const RGBAColor& color) {
+	assert(m_pixels);
+	assert(x < m_w);
+	assert(y < m_h);
+
+	uint8_t* data = &m_pixels[(m_h - y - 1) * 4 * m_w + 4 * x];
+	uint32_t packed_color = SDL_MapRGBA(&rgba_format(), color.r, color.g, color.b, color.a);
+	*(reinterpret_cast<uint32_t *>(data)) = packed_color;
 }
 
 
 void Texture::setup_gl() {
 	glBindFramebuffer(GL_FRAMEBUFFER, GlFramebuffer::instance().id());
 	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0);
+	glViewport(0, 0, m_w, m_h);
+}
+
+void Texture::do_blit(const FloatRect& dst_rect,
+                     const BlitSource& texture,
+                     float opacity,
+                     BlendMode blend_mode) {
+	setup_gl();
+	VanillaBlitProgram::instance().draw(dst_rect, 0.f, texture, opacity, blend_mode);
+}
+
+void Texture::do_blit_blended(const FloatRect& dst_rect,
+                              const BlitSource& texture,
+                              const BlitSource& mask,
+                              const RGBColor& blend) {
+
+	setup_gl();
+	BlendedBlitProgram::instance().draw(dst_rect, 0.f, texture, mask, blend);
+}
+
+void Texture::do_blit_monochrome(const FloatRect& dst_rect,
+                                 const BlitSource& texture,
+                                 const RGBAColor& blend) {
+	setup_gl();
+	MonochromeBlitProgram::instance().draw(dst_rect, 0.f, texture, blend);
+}
+
+void
+Texture::do_draw_line(const FloatPoint& start, const FloatPoint& end, const RGBColor& color, int width) {
+	setup_gl();
+	DrawLineProgram::instance().draw(start, end, 0.f, color, width);
+}
+
+void
+Texture::do_fill_rect(const FloatRect& dst_rect, const RGBAColor& color, BlendMode blend_mode) {
+	setup_gl();
+	FillRectProgram::instance().draw(dst_rect, 0.f, color, blend_mode);
 }

=== modified file 'src/graphic/texture.h'
--- src/graphic/texture.h	2014-12-07 21:34:11 +0000
+++ src/graphic/texture.h	2015-02-21 07:13:48 +0000
@@ -46,8 +46,6 @@
 	// Implements Surface
 	int width() const override;
 	int height() const override;
-	void setup_gl() override;
-	void pixel_to_gl(float* x, float* y) const override;
 
 	// Implements Image.
 	int get_gl_texture() const override;
@@ -69,30 +67,39 @@
 		Unlock_NoChange
 	};
 
-	/// This returns the pixel format for direct pixel access.
-	const SDL_PixelFormat & format() const;
-
-	// Number of bytes per row.
-	uint16_t get_pitch() const;
-
-	// Pointer to the raw pixel data. May only be called inside lock/unlock
-	// pairs.
-	uint8_t * get_pixels() const;
-
 	// Lock/Unlock pairs must guard any of the direct pixel access using the
 	// functions below. Lock/Unlock pairs cannot be nested.
 	void lock();
 	void unlock(UnlockMode);
 
-	// Returns the color of the pixel as a value as defined by 'format()'.
-	uint32_t get_pixel(uint16_t x, uint16_t y);
+	// Returns the color of the pixel.
+	RGBAColor get_pixel(uint16_t x, uint16_t y);
 
 	// Sets the pixel to the 'clr'.
-	void set_pixel(uint16_t x, uint16_t y, uint32_t clr);
+	void set_pixel(uint16_t x, uint16_t y, const RGBAColor& color);
 
 private:
+	// Configures OpenGL to draw to this surface.
+	void setup_gl();
 	void init(uint16_t w, uint16_t h);
 
+	// Implements surface.
+	void do_blit(const FloatRect& dst_rect,
+	             const BlitSource& texture,
+	             float opacity,
+	             BlendMode blend_mode) override;
+	void do_blit_blended(const FloatRect& dst_rect,
+	                     const BlitSource& texture,
+	                     const BlitSource& mask,
+	                     const RGBColor& blend) override;
+	void do_blit_monochrome(const FloatRect& dst_rect,
+	                        const BlitSource& texture,
+	                        const RGBAColor& blend) override;
+	void
+	do_draw_line(const FloatPoint& start, const FloatPoint& end, const RGBColor& color, int width) override;
+	void
+	do_fill_rect(const FloatRect& dst_rect, const RGBAColor& color, BlendMode blend_mode) override;
+
 	// Width and height.
 	int m_w, m_h;
 

=== modified file 'src/graphic/texture_atlas.cc'
--- src/graphic/texture_atlas.cc	2014-12-07 20:13:27 +0000
+++ src/graphic/texture_atlas.cc	2015-02-21 07:13:48 +0000
@@ -46,7 +46,7 @@
 {
 }
 
-void TextureAtlas::add(const Texture& texture) {
+void TextureAtlas::add(const Image& texture) {
 	blocks_.emplace_back(next_index_++, &texture);
 }
 
@@ -133,7 +133,7 @@
 	}
 
 	std::unique_ptr<Texture> packed_texture(new Texture(root->r.w, root->r.h));
-	fill_rect(Rect(0, 0, root->r.w, root->r.h), RGBAColor(0, 0, 0, 0), packed_texture.get());
+	packed_texture->fill_rect(Rect(0, 0, root->r.w, root->r.h), RGBAColor(0, 0, 0, 0));
 
 	// Sort blocks by index so that they come back in the correct ordering.
 	std::sort(blocks_.begin(), blocks_.end(), [](const Block& i, const Block& j) {
@@ -141,16 +141,16 @@
 	});
 
 	for (Block& block : blocks_) {
-		blit(Rect(block.node->r.x, block.node->r.y, block.texture->width(), block.texture->height()),
-		     *block.texture,
-		     Rect(0, 0, block.texture->width(), block.texture->height()),
-		     1.,
-		     BlendMode::UseAlpha,
-		     packed_texture.get());
+		packed_texture->blit(
+		   Rect(block.node->r.x, block.node->r.y, block.texture->width(), block.texture->height()),
+		   *block.texture,
+		   Rect(0, 0, block.texture->width(), block.texture->height()),
+		   1.,
+		   BlendMode::UseAlpha);
 
 		textures->emplace_back(new Texture(
 		   packed_texture->get_gl_texture(),
-		   Rect(block.node->r.top_left(), block.texture->width(), block.texture->height()),
+		   Rect(block.node->r.origin(), block.texture->width(), block.texture->height()),
 		   root->r.w,
 		   root->r.h));
 	}

=== modified file 'src/graphic/texture_atlas.h'
--- src/graphic/texture_atlas.h	2014-11-28 07:14:26 +0000
+++ src/graphic/texture_atlas.h	2015-02-21 07:13:48 +0000
@@ -35,7 +35,7 @@
 	// Add 'texture' as one of the textures to be packed. Ownership is
 	// not taken, but 'texture' must be valid until pack() has been
 	// called.
-	void add(const Texture& texture);
+	void add(const Image& texture);
 
 	// Packs the textures and returns the packed texture. 'textures'
 	// contains the individual sub textures (that do not own their
@@ -56,12 +56,12 @@
 	};
 
 	struct Block {
-		Block(int init_index, const Texture* init_texture)
+		Block(int init_index, const Image* init_texture)
 		   : index(init_index), texture(init_texture) {
 		}
 
 		int index;
-		const Texture* texture;
+		const Image* texture;
 		Node* node;
 	};
 

=== modified file 'src/logic/CMakeLists.txt'
--- src/logic/CMakeLists.txt	2015-02-09 05:57:08 +0000
+++ src/logic/CMakeLists.txt	2015-02-21 07:13:48 +0000
@@ -26,6 +26,9 @@
     single_player_game_settings_provider.cc
     single_player_game_settings_provider.h
   DEPENDS
+    io_filesystem
+    scripting_lua_interface
+    scripting_lua_table
     logic
     ai
 )

=== modified file 'src/logic/ship.cc'
--- src/logic/ship.cc	2015-02-05 12:11:20 +0000
+++ src/logic/ship.cc	2015-02-21 07:13:48 +0000
@@ -648,12 +648,11 @@
 				Worker* worker;
 				m_items.at(i).get(game, &ware, &worker);
 				if (ware) {
-					// no, we don't transfer the wares, we create new ones out of air and remove the old
-					// ones ;)
-					WaresQueue& wq = cs->waresqueue(ware->descr_index());
-					const uint32_t max = wq.get_max_fill();
+					// no, we don't transfer the wares, we create new ones out of
+					// air and remove the old ones ;)
+					WaresQueue & wq = cs->waresqueue(ware->descr_index());
 					const uint32_t cur = wq.get_filled();
-					assert(max > cur);
+					assert(wq.get_max_fill() > cur);
 					wq.set_filled(cur + 1);
 					m_items.at(i).remove(game);
 					m_items.resize(i);

=== modified file 'src/logic/soldier.cc'
--- src/logic/soldier.cc	2014-12-08 11:08:38 +0000
+++ src/logic/soldier.cc	2015-02-21 07:13:48 +0000
@@ -632,7 +632,7 @@
 	uint32_t health_width = 2 * (w - 1) * m_hp_current / get_max_hitpoints();
 	Rect energy_inner(Point(pt.x - w + 1, pt.y + 1), health_width, 3);
 	Rect energy_complement
-		(energy_inner.top_left() + Point(health_width, 0), 2 * (w - 1) - health_width, 3);
+		(energy_inner.origin() + Point(health_width, 0), 2 * (w - 1) - health_width, 3);
 	const RGBColor & color = owner().get_playercolor();
 	RGBColor complement_color;
 

=== modified file 'src/ui_basic/panel.cc'
--- src/ui_basic/panel.cc	2014-12-06 12:22:35 +0000
+++ src/ui_basic/panel.cc	2015-02-21 07:13:48 +0000
@@ -1201,7 +1201,7 @@
 	Rect r
 		(WLApplication::get()->get_mouse_position() + Point(2, 32),
 		 tip_width, tip_height);
-	const Point tooltip_bottom_right = r.bottom_right();
+	const Point tooltip_bottom_right = r.opposite_of_origin();
 	const Point screen_bottom_right(g_gr->get_xres(), g_gr->get_yres());
 	if (screen_bottom_right.x < tooltip_bottom_right.x)
 		r.x -=  4 + r.w;
@@ -1210,7 +1210,7 @@
 
 	dst.fill_rect(r, RGBColor(63, 52, 34));
 	dst.draw_rect(r, RGBColor(0, 0, 0));
-	dst.blit(r.top_left() + Point(2, 2), rendered_text);
+	dst.blit(r.origin() + Point(2, 2), rendered_text);
 	return true;
 }
 

=== modified file 'src/ui_basic/scrollbar.cc'
--- src/ui_basic/scrollbar.cc	2014-11-22 10:18:20 +0000
+++ src/ui_basic/scrollbar.cc	2015-02-21 07:13:48 +0000
@@ -258,7 +258,7 @@
 		uint16_t cpw = pic->width();
 		uint16_t cph = pic->height();
 
-		dst.blit(r.top_left() + Point((r.w - cpw) / 2, (r.h - cph) / 2), pic);
+		dst.blit(r.origin() + Point((r.w - cpw) / 2, (r.h - cph) / 2), pic);
 	}
 
 	// Draw border
@@ -266,35 +266,35 @@
 
 	if (area != m_pressed) {
 		// top edge
-		dst.brighten_rect(Rect(r.top_left(), r.w, 2), BUTTON_EDGE_BRIGHT_FACTOR);
+		dst.brighten_rect(Rect(r.origin(), r.w, 2), BUTTON_EDGE_BRIGHT_FACTOR);
 		// left edge
 		dst.brighten_rect
-			(Rect(r.top_left() + Point(0, 2), 2, r.h - 2), BUTTON_EDGE_BRIGHT_FACTOR);
+			(Rect(r.origin() + Point(0, 2), 2, r.h - 2), BUTTON_EDGE_BRIGHT_FACTOR);
 		// bottom edge
-		dst.fill_rect(Rect(r.top_left() + Point(2, r.h - 2), r.w - 2, 1), black);
-		dst.fill_rect(Rect(r.top_left() + Point(1, r.h - 1), r.w - 1, 1), black);
+		dst.fill_rect(Rect(r.origin() + Point(2, r.h - 2), r.w - 2, 1), black);
+		dst.fill_rect(Rect(r.origin() + Point(1, r.h - 1), r.w - 1, 1), black);
 		// right edge
-		dst.fill_rect(Rect(r.top_left() + Point(r.w - 2, 2), 1, r.h - 2), black);
-		dst.fill_rect(Rect(r.top_left() + Point(r.w - 1, 1), 1, r.h - 1), black);
+		dst.fill_rect(Rect(r.origin() + Point(r.w - 2, 2), 1, r.h - 2), black);
+		dst.fill_rect(Rect(r.origin() + Point(r.w - 1, 1), 1, r.h - 1), black);
 	} else {
 		// bottom edge
 		dst.brighten_rect
-			(Rect(r.top_left() + Point(0, r.h - 2), r.w, 2), BUTTON_EDGE_BRIGHT_FACTOR);
+			(Rect(r.origin() + Point(0, r.h - 2), r.w, 2), BUTTON_EDGE_BRIGHT_FACTOR);
 		// right edge
 		dst.brighten_rect
-			(Rect(r.top_left() + Point(r.w - 2, 0), 2, r.h - 2), BUTTON_EDGE_BRIGHT_FACTOR);
+			(Rect(r.origin() + Point(r.w - 2, 0), 2, r.h - 2), BUTTON_EDGE_BRIGHT_FACTOR);
 		// top edge
-		dst.fill_rect(Rect(r.top_left(), r.w - 1, 1), black);
-		dst.fill_rect(Rect(r.top_left() + Point(0, 1), r.w - 2, 1), black);
+		dst.fill_rect(Rect(r.origin(), r.w - 1, 1), black);
+		dst.fill_rect(Rect(r.origin() + Point(0, 1), r.w - 2, 1), black);
 		// left edge
-		dst.fill_rect(Rect(r.top_left(), 1, r.h - 1), black);
-		dst.fill_rect(Rect(r.top_left() + Point(1, 0), 1, r.h - 2), black);
+		dst.fill_rect(Rect(r.origin(), 1, r.h - 1), black);
+		dst.fill_rect(Rect(r.origin() + Point(1, 0), 1, r.h - 2), black);
 	}
 }
 
 
 void Scrollbar::draw_area(RenderTarget & dst, const Area area, const Rect r) {
-	dst.tile(r, m_pic_background, Point(get_x(), get_y()) + r.top_left());
+	dst.tile(r, m_pic_background, Point(get_x(), get_y()) + r.origin());
 
 	if (area == m_pressed)
 		dst.brighten_rect(r, BUTTON_EDGE_BRIGHT_FACTOR);

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2015-02-09 05:57:08 +0000
+++ src/wui/interactive_base.cc	2015-02-21 07:13:48 +0000
@@ -902,10 +902,8 @@
 			return true;
 #ifndef NDEBUG  //  only in debug builds
 		case SDLK_F6:
-			if (get_display_flag(dfDebug)) {
-				GameChatMenu::create_script_console(
-				   this, m_debugconsole, *DebugConsole::get_chat_provider());
-			}
+			GameChatMenu::create_script_console(
+				this, m_debugconsole, *DebugConsole::get_chat_provider());
 			return true;
 #endif
 		default:

=== modified file 'src/wui/interactive_gamebase.h'
--- src/wui/interactive_gamebase.h	2015-02-16 12:42:35 +0000
+++ src/wui/interactive_gamebase.h	2015-02-21 07:13:48 +0000
@@ -83,7 +83,8 @@
 
 	void show_game_summary();
 	void postload() override;
-	void start() override {};
+	void start() override {}
+
 protected:
 	void draw_overlay(RenderTarget &) override;
 	virtual int32_t calculate_buildcaps(const Widelands::TCoords<Widelands::FCoords> c) = 0;

=== modified file 'src/wui/minimap.cc'
--- src/wui/minimap.cc	2014-12-07 20:52:55 +0000
+++ src/wui/minimap.cc	2015-02-21 07:13:48 +0000
@@ -62,14 +62,14 @@
 
 void MiniMap::View::draw(RenderTarget & dst)
 {
-	std::unique_ptr<Texture> texture(
+	minimap_image_ =
 	   draw_minimap(m_ibase.egbase(),
 	                m_ibase.get_player(),
 	                (*m_flags) & (MiniMapLayer::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 | MiniMapLayer::ViewWindow));
-	dst.blit(Point(), texture.get());
+	                *m_flags | MiniMapLayer::ViewWindow);
+	dst.blit(Point(), minimap_image_.get());
 }
 
 

=== modified file 'src/wui/minimap.h'
--- src/wui/minimap.h	2014-09-27 18:53:55 +0000
+++ src/wui/minimap.h	2015-02-21 07:13:48 +0000
@@ -20,6 +20,8 @@
 #ifndef WL_WUI_MINIMAP_H
 #define WL_WUI_MINIMAP_H
 
+#include <memory>
+
 #include <boost/signals2.hpp>
 
 #include "graphic/minimap_renderer.h"
@@ -76,13 +78,17 @@
 
 		void set_zoom(int32_t z);
 
-
 	private:
 		InteractiveBase & m_ibase;
 		int32_t                m_viewx, m_viewy;
 		const Image* m_pic_map_spot;
+
+		// This needs to be owned since it will be rendered by the RenderQueue
+		// later, so it must be valid for the whole frame.
+		std::unique_ptr<Texture> minimap_image_;
+
 	public:
-		MiniMapLayer * m_flags;
+		MiniMapLayer* m_flags;
 	};
 
 	uint32_t number_of_buttons_per_row() const;

=== modified file 'src/wui/plot_area.cc'
--- src/wui/plot_area.cc	2014-12-11 12:38:10 +0000
+++ src/wui/plot_area.cc	2015-02-21 07:13:48 +0000
@@ -185,35 +185,34 @@
 
 	// Draw coordinate system
 	// X Axis
-	dst.draw_line
-		(spacing,                        inner_h - space_at_bottom,
-		 inner_w - space_at_right, inner_h - space_at_bottom,
-		 LINE_COLOR, 2);
+	dst.draw_line(Point(spacing, inner_h - space_at_bottom),
+	              Point(inner_w - space_at_right, inner_h - space_at_bottom),
+	              LINE_COLOR,
+	              2);
 	// Arrow
-	dst.draw_line
-		(spacing,     inner_h - space_at_bottom,
-		 spacing + 5, inner_h - space_at_bottom - 3,
-		 LINE_COLOR, 2);
-	dst.draw_line
-		(spacing,     inner_h - space_at_bottom,
-		 spacing + 5, inner_h - space_at_bottom + 3,
-		 LINE_COLOR, 2);
+	dst.draw_line(Point(spacing, inner_h - space_at_bottom),
+	              Point(spacing + 5, inner_h - space_at_bottom - 3),
+	              LINE_COLOR,
+	              2);
+	dst.draw_line(Point(spacing, inner_h - space_at_bottom),
+	              Point(spacing + 5, inner_h - space_at_bottom + 3),
+	              LINE_COLOR,
+	              2);
 	//  Y Axis
-	dst.draw_line
-		(inner_w - space_at_right, spacing,
-		 inner_w - space_at_right,
-		 inner_h - space_at_bottom,
-		 LINE_COLOR, 2);
+	dst.draw_line(Point(inner_w - space_at_right, spacing),
+	              Point(inner_w - space_at_right, inner_h - space_at_bottom),
+	              LINE_COLOR,
+	              2);
 	//  No Arrow here, since this doesn't continue.
 
 	float sub = (xline_length - space_left_of_label) / how_many_ticks;
 	float posx = inner_w - space_at_right;
 
 	for (uint32_t i = 0; i <= how_many_ticks; ++i) {
-		dst.draw_line
-			(static_cast<int32_t>(posx), inner_h - space_at_bottom,
-			 static_cast<int32_t>(posx), inner_h - space_at_bottom + 3,
-			 LINE_COLOR, 2);
+		dst.draw_line(Point(static_cast<int32_t>(posx), inner_h - space_at_bottom),
+		              Point(static_cast<int32_t>(posx), inner_h - space_at_bottom + 3),
+		              LINE_COLOR,
+		              2);
 
 		// The space at the end is intentional to have the tick centered
 		// over the number, not to the left
@@ -227,16 +226,15 @@
 	}
 
 	//  draw yticks, one at full, one at half
-	dst.draw_line
-		(inner_w - space_at_right,    spacing,
-		 inner_w - space_at_right -3, spacing,
-		 LINE_COLOR, 2);
-	dst.draw_line
-		(inner_w - space_at_right,
-		 spacing + ((inner_h - space_at_bottom) - spacing) / 2,
-		 inner_w - space_at_right - 3,
-		 spacing + ((inner_h - space_at_bottom) - spacing) / 2,
-		 LINE_COLOR, 2);
+	dst.draw_line(Point(inner_w - space_at_right, spacing),
+	              Point(inner_w - space_at_right - 3, spacing),
+	              LINE_COLOR,
+	              2);
+	dst.draw_line(
+	   Point(inner_w - space_at_right, spacing + ((inner_h - space_at_bottom) - spacing) / 2),
+	   Point(inner_w - space_at_right - 3, spacing + ((inner_h - space_at_bottom) - spacing) / 2),
+	   LINE_COLOR,
+	   2);
 
 	//  print the used unit
 	const Image* xtick = UI::g_fh1->render(xtick_text_style(get_unit_name(unit)));
@@ -435,7 +433,7 @@
 			cury -= static_cast<int32_t>(length_y);
 		}
 
-		dst.draw_line(lx, ly, curx, cury, color, 2);
+		dst.draw_line(Point(lx, ly), Point(curx, cury), color, 2);
 
 		posx -= sub;
 
@@ -514,12 +512,10 @@
 	draw_diagram(time_ms, get_inner_w(), get_inner_h(), xline_length, dst);
 
 	// draw zero line
-	dst.draw_line
-		(get_inner_w() - space_at_right,
-		 yoffset,
-		 get_inner_w() - space_at_right - xline_length,
-		 yoffset,
-		 ZERO_LINE_COLOR, 2);
+	dst.draw_line(Point(get_inner_w() - space_at_right, yoffset),
+	              Point(get_inner_w() - space_at_right - xline_length, yoffset),
+	              ZERO_LINE_COLOR,
+	              2);
 
 	// How many do we take together when relative ploting
 	const int32_t how_many = calc_how_many(time_ms, m_sample_rate);

=== modified file 'src/wui/soldierlist.cc'
--- src/wui/soldierlist.cc	2014-12-06 12:22:35 +0000
+++ src/wui/soldierlist.cc	2015-02-21 07:13:48 +0000
@@ -495,8 +495,10 @@
 }
 
 void SoldierList::set_soldier_preference(int32_t changed_to) {
+#ifndef NDEBUG
 	upcast(Widelands::MilitarySite, ms, &m_building);
 	assert(ms);
+#endif
 	m_igb.game().send_player_militarysite_set_soldier_preference
 		(m_building, changed_to == 0 ?
 			Widelands::MilitarySite::kPrefersRookies:


Follow ups