← Back to team overview

widelands-dev team mailing list archive

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

 

Some minor nits with my proofreader glasses on.

Diff comments:

> === modified file 'src/base/rect.h'
> --- src/base/rect.h	2014-11-26 19:53:52 +0000
> +++ src/base/rect.h	2015-03-01 10:31:43 +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-03-01 10:31:43 +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-24 13:51:38 +0000
> +++ src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc	2015-03-01 10:31:43 +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::kImpassable) {
> -				blit(Rect(pt.x, pt.y, impassable->width(), impassable->height()),
> -				     *impassable,
> -				     Rect(0, 0, impassable->width(), impassable->height()),
> -				     1.,
> -				     BlendMode::UseAlpha,
> -				     texture);
> +				texture->blit(Rect(pt.x, pt.y, impassable->width(), impassable->height()),
> +				              *impassable,
> +				              Rect(0, 0, impassable->width(), impassable->height()),
> +				              1.,
> +				              BlendMode::UseAlpha);
>  				pt.x += kSmallPicWidth + 1;
>  				/** TRANSLATORS: This is a terrain type tooltip in the editor */
>  				tooltips.push_back(_("impassable"));
>  			}
>  			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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/color.cc	2015-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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);
> +

Why is this commented out?

> +	// 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-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/blit_program.cc	2015-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/blit_program.h	2015-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/dither_program.cc	2015-03-01 10:31:43 +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.);
>  }
>  )";
>  
> @@ -64,12 +66,18 @@
>  varying vec2 var_texture_position;
>  varying vec2 var_texture_offset;
>  
> +// TODO(sirver): This is a hack to make sure we are sampling inside of the
> +// terrain texture. This is a common problem with OpenGL and texture atlases.
> +#define MARGIN 1e-2
> +

Code style: constexpr float kMargin

>  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;
> +	vec2 texture_fract = clamp(
> +			fract(var_texture_position),
> +			vec2(MARGIN, MARGIN),
> +			vec2(1. - MARGIN, 1. - MARGIN));
> +	vec4 clr = texture2D(u_terrain_texture, var_texture_offset + u_texture_dimensions * texture_fract);
> +	gl_FragColor = vec4(clr.rgb * var_brightness,
> +			1. - texture2D(u_dither_texture, var_dither_texture_position).a);
>  }
>  )";
>  
> @@ -87,12 +95,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 +125,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 +158,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 +174,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 +198,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 +218,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 +277,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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/dither_program.h	2015-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/draw_line_program.cc	2015-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/draw_line_program.h	2015-03-01 10:31:43 +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-03-01 09:21:20 +0000
> +++ src/graphic/gl/fields_to_draw.h	2015-03-01 10:31:43 +0000
> @@ -43,14 +43,20 @@
>  		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;
> +		const size_t size = w_ * h_;
> +		if (fields_.size() != size) {
> +			fields_.resize(size);
> +		}
>  	}
>  
>  	// Calculates the index of the given field with ('fx', 'fy') being geometric
> @@ -84,14 +90,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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/fill_rect_program.cc	2015-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/fill_rect_program.h	2015-03-01 10:31:43 +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-03-01 09:21:20 +0000
> +++ src/graphic/gl/road_program.cc	2015-03-01 10:31:43 +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,
> @@ -120,32 +124,39 @@
>  
>  	const auto& texture_rect = texture.texture_coordinates();
>  
> +	// TODO(sirver): This is a hack to make sure we are sampling inside of the
> +	// terrain texture. This is a common problem with OpenGL and texture atlases.
> +	const float MARGIN = 5e-2;
> +

Code style: constexpr float kMargin

>  	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.x + MARGIN,
> +	   texture_rect.y + MARGIN,
>  	   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.x + MARGIN,
> +	   texture_rect.y + texture_rect.h - MARGIN,
>  	   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,
>  	   end.pixel_y + road_overshoot_y + road_thickness_y,
> -	   texture_rect.x + texture_rect.w,
> -	   texture_rect.y,
> +	   texture_rect.x + texture_rect.w - MARGIN,
> +	   texture_rect.y + MARGIN,
>  	   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
> @@ -157,14 +168,18 @@
>  	vertices_.emplace_back(PerVertexData{
>  	   end.pixel_x + road_overshoot_x - road_thickness_x,
>  	   end.pixel_y + road_overshoot_y - road_thickness_y,
> -	   texture_rect.x + texture_rect.w,
> -	   texture_rect.y + texture_rect.h,
> +	   texture_rect.x + texture_rect.w - MARGIN,
> +	   texture_rect.y + texture_rect.h - MARGIN,
>  	   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 +190,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 +206,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 +223,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 +242,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 +260,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-03-01 09:21:20 +0000
> +++ src/graphic/gl/road_program.h	2015-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/terrain_program.cc	2015-03-01 10:31:43 +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.);
>  }
>  )";
>  
> @@ -63,9 +65,20 @@
>  varying vec2 var_texture_position;
>  varying vec2 var_texture_offset;
>  
> +// TODO(sirver): This is a hack to make sure we are sampling inside of the
> +// terrain texture. This is a common problem with OpenGL and texture atlases.
> +#define MARGIN 1e-2
> +

Code style: constexpr float kMargin

>  void main() {
> -	vec4 clr = texture2D(u_terrain_texture,
> -			var_texture_offset + u_texture_dimensions * fract(var_texture_position));
> +	// 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.
> +	vec2 texture_fract = clamp(
> +			fract(var_texture_position),
> +			vec2(MARGIN, MARGIN),
> +			vec2(1. - MARGIN, 1. - MARGIN));
> +	vec4 clr = texture2D(u_terrain_texture, var_texture_offset + u_texture_dimensions * texture_fract);
>  	clr.rgb *= var_brightness;
>  	gl_FragColor = clr;
>  }
> @@ -83,9 +96,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 +107,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 +153,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 +178,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 +188,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 +196,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	2015-03-01 09:21:20 +0000
> +++ src/graphic/gl/terrain_program.h	2015-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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:

Can this be renamed back to Buffer now? Honest question ;)

> -	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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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,64 @@
>  	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) {
> +					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 +218,15 @@
>  	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);
> -	assert(texture->format().BytesPerPixel == sizeof(uint32_t));
> -
> -	fill_rect(Rect(0, 0, texture->width(), texture->height()), RGBAColor(0, 0, 0, 255), texture);
> +	std::unique_ptr<Texture> texture(new Texture(map_w, map_h));
> +
> +	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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/texture_atlas.cc	2015-03-01 10:31:43 +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	2015-03-01 09:21:20 +0000
> +++ src/graphic/texture_atlas.h	2015-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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-03-01 10:31:43 +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:
> 


-- 
https://code.launchpad.net/~widelands-dev/widelands/render_queue/+merge/250524
Your team Widelands Developers is subscribed to branch lp:~widelands-dev/widelands/render_queue.


References