← Back to team overview

widelands-dev team mailing list archive

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

 

SirVer has proposed merging lp:~widelands-dev/widelands/move_drawing_out into lp:widelands with lp:~widelands-dev/widelands/remove_in_memory_image as a prerequisite.

Requested reviews:
  Widelands Developers (widelands-dev)

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

- Moves drawing functions out of Surface into stand alone methods.
- Image is now a base class of Surface. This needed some unfortunate multiple inheritance, but I believe that this can be mended with the new font renderer being merged soon.
- Removed InMemoryImage. Plain Texture can now be used instead.
- Use glReadPixels() for screenshots. This is GL ES 2 compatible and got rid of pixel access to the screen.
-  Move TextureCache out of graphics and into FontHandler1. The only place that is caching surfaces now is the new text renderer.

---
This refactoring fell into place rather easily (except for the multiple inheritance, but that is just two interfaces, so it should not matter). I think I am now in the place where I can look into bunching up blit calls. This is the final optimization that I want to do in the graphics engine for now. 

-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/move_drawing_out into lp:widelands.
=== modified file 'src/editor/CMakeLists.txt'
--- src/editor/CMakeLists.txt	2014-11-28 08:33:11 +0000
+++ src/editor/CMakeLists.txt	2014-12-08 05:59:46 +0000
@@ -90,7 +90,6 @@
     base_macros
     base_scoped_timer
     graphic
-    graphic_image
     graphic_surface
     io_filesystem
     logic

=== modified file 'src/editor/ui_menus/editor_tool_change_resources_options_menu.cc'
--- src/editor/ui_menus/editor_tool_change_resources_options_menu.cc	2014-11-30 18:49:38 +0000
+++ src/editor/ui_menus/editor_tool_change_resources_options_menu.cc	2014-12-08 05:59:46 +0000
@@ -135,7 +135,7 @@
 	Widelands::ResourceIndex const nr_resources = world.get_nr_resources();
 
 	//  Find the maximal width and height for the resource pictures.
-	uint16_t resource_pic_max_width = 0, resource_pic_max_height = 0;
+	int resource_pic_max_width = 0, resource_pic_max_height = 0;
 	for (Widelands::ResourceIndex i = 0; i < nr_resources; ++i) {
 		const Image* pic = g_gr->images().get(world.get_resource(i)->get_editor_pic(100000));
 		resource_pic_max_width  = std::max(resource_pic_max_width,  pic->width());

=== modified file 'src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc'
--- src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc	2014-12-08 05:59:45 +0000
+++ src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc	2014-12-08 05:59:46 +0000
@@ -28,7 +28,6 @@
 #include "editor/editorinteractive.h"
 #include "editor/tools/editor_set_terrain_tool.h"
 #include "graphic/graphic.h"
-#include "graphic/in_memory_image.h"
 #include "graphic/rendertarget.h"
 #include "graphic/texture.h"
 #include "logic/map.h"
@@ -76,76 +75,82 @@
 
 		const Texture& terrain_texture = terrain_descr.get_texture(0);
 		Texture* texture = new Texture(terrain_texture.width(), terrain_texture.height());
-		texture->blit(Rect(0, 0, terrain_texture.width(), terrain_texture.height()),
-		              &terrain_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);
+		              BlendMode::UseAlpha, texture);
 		Point pt(1, terrain_texture.height() - kSmallPicHeight - 1);
 
 		if (ter_is == TerrainDescription::GREEN) {
-			texture->blit(Rect(pt.x, pt.y, green->width(), green->height()),
-			              green->texture(),
-			              Rect(0, 0, green->width(), green->height()),
-			              1.,
-			              BlendMode::UseAlpha);
+			blit(Rect(pt.x, pt.y, green->width(), green->height()),
+			     *green,
+			     Rect(0, 0, green->width(), green->height()),
+			     1.,
+			     BlendMode::UseAlpha,
+			     texture);
 			pt.x += kSmallPicWidth + 1;
 			/** TRANSLATORS: This is a terrain type tooltip in the editor */
 			tooltips.push_back(_("arable"));
 		} else {
 			if (ter_is & TerrainDescription::WATER) {
-				texture->blit(Rect(pt.x, pt.y, water->width(), water->height()),
-				              water->texture(),
-				              Rect(0, 0, water->width(), water->height()),
-				              1.,
-				              BlendMode::UseAlpha);
+				blit(Rect(pt.x, pt.y, water->width(), water->height()),
+				     *water,
+				     Rect(0, 0, water->width(), water->height()),
+				     1.,
+				     BlendMode::UseAlpha,
+				     texture);
 				pt.x += kSmallPicWidth + 1;
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				tooltips.push_back(_("aquatic"));
 			}
 			else if (ter_is & TerrainDescription::MOUNTAIN) {
-				texture->blit(Rect(pt.x, pt.y, mountain->width(), mountain->height()),
-				              mountain->texture(),
-				              Rect(0, 0, mountain->width(), mountain->height()),
-				              1.,
-				              BlendMode::UseAlpha);
+				blit(Rect(pt.x, pt.y, mountain->width(), mountain->height()),
+				     *mountain,
+				     Rect(0, 0, mountain->width(), mountain->height()),
+				     1.,
+				     BlendMode::UseAlpha,
+				     texture);
 				pt.x += kSmallPicWidth + 1;
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				tooltips.push_back(_("mountainous"));
 			}
 			if (ter_is & TerrainDescription::ACID) {
-				texture->blit(Rect(pt.x, pt.y, dead->width(), dead->height()),
-				              dead->texture(),
-				              Rect(0, 0, dead->width(), dead->height()),
-				              1.,
-				              BlendMode::UseAlpha);
+				blit(Rect(pt.x, pt.y, dead->width(), dead->height()),
+				     *dead,
+				     Rect(0, 0, dead->width(), dead->height()),
+				     1.,
+				     BlendMode::UseAlpha,
+				     texture);
 				pt.x += kSmallPicWidth + 1;
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				tooltips.push_back(_("dead"));
 			}
 			if (ter_is & TerrainDescription::UNPASSABLE) {
-				texture->blit(Rect(pt.x, pt.y, unpassable->width(), unpassable->height()),
-				              unpassable->texture(),
-				              Rect(0, 0, unpassable->width(), unpassable->height()),
-				              1.,
-				              BlendMode::UseAlpha);
+				blit(Rect(pt.x, pt.y, unpassable->width(), unpassable->height()),
+				     *unpassable,
+				     Rect(0, 0, unpassable->width(), unpassable->height()),
+				     1.,
+				     BlendMode::UseAlpha,
+				     texture);
 				pt.x += kSmallPicWidth + 1;
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				tooltips.push_back(_("unpassable"));
 			}
 			if (ter_is & TerrainDescription::DRY) {
-				texture->blit(Rect(pt.x, pt.y, dry->width(), dry->height()),
-				              dry->texture(),
-				              Rect(0, 0, dry->width(), dry->height()),
-				              1.,
-				              BlendMode::UseAlpha);
+				blit(Rect(pt.x, pt.y, dry->width(), dry->height()),
+				     *dry,
+				     Rect(0, 0, dry->width(), dry->height()),
+				     1.,
+				     BlendMode::UseAlpha,
+				     texture);
 				/** TRANSLATORS: This is a terrain type tooltip in the editor */
 				 tooltips.push_back(_("treeless"));
 			}
 		}
 
 		// Make sure we delete this later on.
-		offscreen_images->emplace_back(new_in_memory_image(texture));
+		offscreen_images->emplace_back(texture);
 		break;
 	}
 	/** TRANSLATORS: %1% = terrain name, %2% = list of terrain types  */

=== modified file 'src/game_io/game_preload_packet.cc'
--- src/game_io/game_preload_packet.cc	2014-11-23 11:56:13 +0000
+++ src/game_io/game_preload_packet.cc	2014-12-08 05:59:46 +0000
@@ -26,7 +26,6 @@
 
 #include "base/time_string.h"
 #include "graphic/graphic.h"
-#include "graphic/in_memory_image.h"
 #include "graphic/minimap_renderer.h"
 #include "logic/game.h"
 #include "logic/game_data_error.h"

=== modified file 'src/graphic/CMakeLists.txt'
--- src/graphic/CMakeLists.txt	2014-12-08 05:59:45 +0000
+++ src/graphic/CMakeLists.txt	2014-12-08 05:59:46 +0000
@@ -25,9 +25,8 @@
     io_stream
 )
 
-wl_library(graphic_image
+wl_library(graphic_image_cache
   SRCS
-    image.h
     image_cache.cc
     image_cache.h
   USES_SDL2
@@ -69,6 +68,7 @@
     gl/draw_rect_program.h
     gl/fill_rect_program.cc
     gl/fill_rect_program.h
+    image.h
     screen.cc
     screen.h
     surface.cc
@@ -135,7 +135,6 @@
     base_macros
     economy
     graphic
-    graphic_image
     graphic_surface
     logic
     wui_mapview_pixelfunctions
@@ -157,8 +156,6 @@
     font_handler1.h
     graphic.cc
     graphic.h
-    in_memory_image.cc
-    in_memory_image.h
     rendertarget.cc
     rendertarget.h
     richtext.cc
@@ -179,7 +176,7 @@
     build_info
     graphic_color
     graphic_gl_utils
-    graphic_image
+    graphic_image_cache
     graphic_image_io
     graphic_surface
     graphic_text

=== modified file 'src/graphic/animation.cc'
--- src/graphic/animation.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/animation.cc	2014-12-08 05:59:46 +0000
@@ -39,7 +39,6 @@
 #include "graphic/image.h"
 #include "graphic/image_cache.h"
 #include "graphic/surface.h"
-#include "graphic/texture_cache.h"
 #include "io/filesystem/layered_filesystem.h"
 #include "logic/bob.h"
 #include "logic/instances.h"
@@ -326,17 +325,19 @@
 	assert(idx < nr_frames());
 
 	if (!hasplrclrs_ || clr == nullptr) {
-		target->blit(Rect(dst.x, dst.y, srcrc.w, srcrc.h),
-		             frames_.at(idx)->texture(),
+		::blit(Rect(dst.x, dst.y, srcrc.w, srcrc.h),
+		     *frames_.at(idx),
+		     srcrc,
+		     1.,
+		     BlendMode::UseAlpha,
+		     target);
+	} else {
+		blit_blended(Rect(dst.x, dst.y, srcrc.w, srcrc.h),
+		             *frames_.at(idx),
+		             *pcmasks_.at(idx),
 		             srcrc,
-		             1.,
-		             BlendMode::UseAlpha);
-	} else {
-		target->blit_blended(Rect(dst.x, dst.y, srcrc.w, srcrc.h),
-		                     frames_.at(idx)->texture(),
-		                     pcmasks_.at(idx)->texture(),
-		                     srcrc,
-		                     *clr);
+		             *clr,
+		             target);
 	}
 }
 

=== modified file 'src/graphic/font_handler.cc'
--- src/graphic/font_handler.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/font_handler.cc	2014-12-08 05:59:46 +0000
@@ -30,7 +30,6 @@
 #include "base/log.h"
 #include "base/wexception.h"
 #include "graphic/graphic.h"
-#include "graphic/in_memory_image.h"
 #include "graphic/rendertarget.h"
 #include "graphic/texture.h"
 #include "graphic/wordwrap.h"
@@ -196,7 +195,7 @@
 		return;
 	}
 
-	lce.image = new_in_memory_image(new Texture(text_surface));
+	lce.image.reset(new Texture(text_surface));
 	lce.width = lce.image->width();
 	lce.height = lce.image->height();
 }

=== modified file 'src/graphic/font_handler1.cc'
--- src/graphic/font_handler1.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/font_handler1.cc	2014-12-08 05:59:46 +0000
@@ -24,6 +24,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/utility.hpp>
 
+#include "base/log.h"
 #include "base/wexception.h"
 #include "graphic/graphic.h"
 #include "graphic/image.h"
@@ -42,22 +43,41 @@
 
 namespace {
 
+/// The size of the richtext surface cache in bytes. All work that the richtext
+//renderer does is / cached in this cache until it overflows.
+const uint32_t RICHTEXT_SURFACE_CACHE = 160 << 20;   // shifting converts to MB
+
 // An Image implementation that recreates a rich text texture when needed on
 // the fly. It is meant to be saved into the ImageCache.
 class RTImage : public Image {
 public:
-	RTImage
-		(const string& ghash, TextureCache* texture_cache, RT::Renderer*
-		 rt_renderer, const string& text, uint16_t gwidth)
-		: hash_(ghash), text_(text), width_(gwidth), texture_cache_(texture_cache),
-		  rt_renderer_(rt_renderer)
-	{}
+	RTImage(const string& ghash,
+	        TextureCache* texture_cache,
+	        RT::Renderer* rt_renderer,
+	        const string& text,
+	        int gwidth)
+	   : hash_(ghash),
+	     text_(text),
+	     width_(gwidth),
+	     texture_cache_(texture_cache),
+	     rt_renderer_(rt_renderer) {
+	}
 	virtual ~RTImage() {}
 
 	// Implements Image.
-	uint16_t width() const override {return texture()->width();}
-	uint16_t height() const override {return texture()->height();}
-	Texture* texture() const override {
+	int width() const override {return texture()->width();}
+	int height() const override {return texture()->height();}
+
+	int get_gl_texture() const override {
+		return texture()->get_gl_texture();
+	}
+
+	const FloatRect& texture_coordinates() const override {
+		return texture()->texture_coordinates();
+	}
+
+private:
+	Texture* texture() const {
 		Texture* surf = texture_cache_->get(hash_);
 		if (surf)
 			return surf;
@@ -67,10 +87,9 @@
 		return surf;
 	}
 
-private:
 	const string hash_;
 	const string text_;
-	uint16_t width_;
+	int width_;
 
 	// Nothing owned.
 	TextureCache* const texture_cache_;
@@ -86,8 +105,11 @@
 // be a problem.
 class FontHandler1 : public IFontHandler1 {
 public:
-	FontHandler1(ImageCache* image_cache, TextureCache* texture_cache, RT::Renderer* renderer) :
-		texture_cache_(texture_cache), image_cache_(image_cache), renderer_(renderer) {}
+	FontHandler1(ImageCache* image_cache)
+	   : texture_cache_(create_texture_cache(RICHTEXT_SURFACE_CACHE)),
+	     renderer_(new RT::Renderer(image_cache, texture_cache_.get())),
+	     image_cache_(image_cache) {
+	}
 	virtual ~FontHandler1() {}
 
 	const Image* render(const string& text, uint16_t w = 0) override {
@@ -96,21 +118,21 @@
 		if (image_cache_->has(hash))
 			return image_cache_->get(hash);
 
-		std::unique_ptr<RTImage> image(new RTImage(hash, texture_cache_, renderer_.get(), text, w));
-		image->texture(); // force the rich text to get rendered in case there is an exception thrown.
+		std::unique_ptr<RTImage> image(
+		   new RTImage(hash, texture_cache_.get(), renderer_.get(), text, w));
+		image->width(); // force the rich text to get rendered in case there is an exception thrown.
 
 		return image_cache_->insert(hash, std::move(image));
 	}
 
 private:
-	TextureCache* const texture_cache_;  // not owned
+	std::unique_ptr<TextureCache> texture_cache_;
+	std::unique_ptr<RT::Renderer> renderer_;
 	ImageCache* const image_cache_;  // not owned
-	std::unique_ptr<RT::Renderer> renderer_;
 };
 
 IFontHandler1 * create_fonthandler(Graphic* gr) {
-	return new FontHandler1(
-	   &gr->images(), &gr->textures(), new RT::Renderer(&gr->images(), &gr->textures()));
+	return new FontHandler1(&gr->images());
 }
 
 IFontHandler1 * g_fh1 = nullptr;

=== modified file 'src/graphic/graphic.cc'
--- src/graphic/graphic.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/graphic.cc	2014-12-08 05:59:46 +0000
@@ -19,6 +19,8 @@
 
 #include "graphic/graphic.h"
 
+#include <memory>
+
 #include "base/log.h"
 #include "base/wexception.h"
 #include "build_info.h"
@@ -30,7 +32,6 @@
 #include "graphic/rendertarget.h"
 #include "graphic/screen.h"
 #include "graphic/texture.h"
-#include "graphic/texture_cache.h"
 #include "io/filesystem/layered_filesystem.h"
 #include "io/streamwrite.h"
 #include "notifications/notifications.h"
@@ -41,10 +42,6 @@
 
 namespace  {
 
-/// The size of the transient (i.e. temporary) surfaces in the cache in bytes.
-/// These are all surfaces that are not loaded from disk.
-const uint32_t TRANSIENT_TEXTURE_CACHE_SIZE = 160 << 20;   // shifting converts to MB
-
 // Sets the icon for the application.
 void set_icon(SDL_Window* sdl_window) {
 #ifndef _WIN32
@@ -66,7 +63,6 @@
    : m_window_mode_width(window_mode_w),
      m_window_mode_height(window_mode_h),
      m_update(true),
-     texture_cache_(create_texture_cache(TRANSIENT_TEXTURE_CACHE_SIZE)),
      image_cache_(new ImageCache()),
      animation_manager_(new AnimationManager())
 {
@@ -144,7 +140,6 @@
 
 Graphic::~Graphic()
 {
-	texture_cache_->flush();
 	// TODO(unknown): this should really not be needed, but currently is :(
 	if (UI::g_fh)
 		UI::g_fh->flush();
@@ -280,8 +275,8 @@
  * @param surf The Surface to save
  * @param sw a StreamWrite where the png is written to
  */
-void Graphic::save_png(const Image* image, StreamWrite * sw) const {
-	save_surface_to_png(image->texture(), sw, COLOR_TYPE::RGBA);
+void Graphic::save_png(Texture* texture, StreamWrite * sw) const {
+	save_to_png(texture, sw, ColorType::RGBA);
 }
 
 /**
@@ -290,7 +285,6 @@
 void Graphic::screenshot(const string& fname) const
 {
 	log("Save screenshot to %s\n", fname.c_str());
-	StreamWrite * sw = g_fs->open_stream_write(fname);
-	save_surface_to_png(screen_.get(), sw, COLOR_TYPE::RGB);
-	delete sw;
+	std::unique_ptr<StreamWrite> sw(g_fs->open_stream_write(fname));
+	save_to_png(screen_->to_texture().get(), sw.get(), ColorType::RGB);
 }

=== modified file 'src/graphic/graphic.h'
--- src/graphic/graphic.h	2014-11-27 21:29:21 +0000
+++ src/graphic/graphic.h	2014-12-08 05:59:46 +0000
@@ -28,12 +28,9 @@
 #include "notifications/notifications.h"
 #include "notifications/note_ids.h"
 
-#define MAX_RECTS 20
-
 class AnimationManager;
 class RenderTarget;
-class Surface;
-class TextureCache;
+class Screen;
 class StreamWrite;
 
 // Will be send whenever the resolution changes.
@@ -73,11 +70,10 @@
 	void refresh();
 	SDL_Window* get_sdlwindow() {return m_sdl_window;}
 
-	TextureCache& textures() const {return *texture_cache_.get();}
 	ImageCache& images() const {return *image_cache_.get();}
 	AnimationManager& animations() const {return *animation_manager_.get();}
 
-	void save_png(const Image*, StreamWrite*) const;
+	void save_png(Texture*, StreamWrite*) const;
 
 	void screenshot(const std::string& fname) const;
 
@@ -91,7 +87,7 @@
 
 	/// This is the main screen Surface.
 	/// A RenderTarget for this can be retrieved with get_render_target()
-	std::unique_ptr<Surface> screen_;
+	std::unique_ptr<Screen> screen_;
 	/// This saves a copy of the screen SDL_Surface. This is needed for
 	/// opengl rendering as the SurfaceOpenGL does not use it. It allows
 	/// manipulation the screen context.
@@ -102,11 +98,9 @@
 	/// This marks the complete screen for updating.
 	bool m_update;
 
-	/// Volatile cache of Hardware dependant textures.
-	std::unique_ptr<TextureCache> texture_cache_;
-	/// Non-volatile cache of hardware independent images. The use the
-	/// texture_cache_ to cache their pixel data.
+	/// Non-volatile cache of independent images.
 	std::unique_ptr<ImageCache> image_cache_;
+
 	/// This holds all animations.
 	std::unique_ptr<AnimationManager> animation_manager_;
 };

=== modified file 'src/graphic/image.h'
--- src/graphic/image.h	2014-12-08 05:59:45 +0000
+++ src/graphic/image.h	2014-12-08 05:59:46 +0000
@@ -25,6 +25,7 @@
 #include <stdint.h>
 
 #include "base/macros.h"
+#include "base/rect.h"
 
 class Texture;
 
@@ -37,9 +38,15 @@
 	Image() = default;
 	virtual ~Image() {}
 
-	virtual uint16_t width() const = 0;
-	virtual uint16_t height() const = 0;
-	virtual Texture* texture() const = 0;
+	// Dimensions of this Image in pixels.
+	virtual int width() const = 0;
+	virtual int height() const = 0;
+
+	// OpenGL texture and texture coordinates backing this Image. This can
+	// change at any time, so do not hold one to this value for more than one
+	// frame.
+	virtual int get_gl_texture() const = 0;
+	virtual const FloatRect& texture_coordinates() const = 0;
 
 private:
 	DISALLOW_COPY_AND_ASSIGN(Image);

=== modified file 'src/graphic/image_cache.cc'
--- src/graphic/image_cache.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/image_cache.cc	2014-12-08 05:59:46 +0000
@@ -26,7 +26,6 @@
 #include "base/log.h"
 #include "graphic/image.h"
 #include "graphic/image_io.h"
-#include "graphic/in_memory_image.h"
 #include "graphic/texture.h"
 
 ImageCache::ImageCache() {
@@ -49,7 +48,7 @@
 const Image* ImageCache::get(const std::string& hash) {
 	ImageMap::const_iterator it = images_.find(hash);
 	if (it == images_.end()) {
-		images_.insert(make_pair(hash, new_in_memory_image(load_image(hash).release())));
+		images_.insert(make_pair(hash, load_image(hash)));
 		return get(hash);
 	}
 	return it->second.get();

=== modified file 'src/graphic/image_io.cc'
--- src/graphic/image_io.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/image_io.cc	2014-12-08 05:59:46 +0000
@@ -33,13 +33,13 @@
 
 namespace {
 
-// A helper function for save_surface_to_png. Writes the compressed data to
+// A helper function for save_to_png. Writes the compressed data to
 // the StreamWrite.
 void png_write_function(png_structp png_ptr, png_bytep png_data, png_size_t length) {
 	static_cast<StreamWrite*>(png_get_io_ptr(png_ptr))->data(png_data, length);
 }
 
-// A helper function for save_surface_to_png.
+// A helper function for save_to_png.
 // Flush function to avoid crashes with default libpng flush function
 void png_flush_function(png_structp png_ptr) {
 	static_cast<StreamWrite*>(png_get_io_ptr(png_ptr))->flush();
@@ -82,23 +82,23 @@
 	return sdlsurf;
 }
 
-bool save_surface_to_png(Surface* surface, StreamWrite* sw, COLOR_TYPE color_type) {
+bool save_to_png(Texture* texture, StreamWrite* sw, ColorType color_type) {
 	png_structp png_ptr = png_create_write_struct(
 	   PNG_LIBPNG_VER_STRING, static_cast<png_voidp>(nullptr), nullptr, nullptr);
 
 	if (!png_ptr)
-		throw wexception("save_surface_to_png: could not create png struct");
+		throw wexception("save_to_png: could not create png struct");
 
 	png_infop info_ptr = png_create_info_struct(png_ptr);
 	if (!info_ptr) {
 		png_destroy_write_struct(&png_ptr, static_cast<png_infopp>(nullptr));
-		throw wexception("save_surface_to_png: could not create png info struct");
+		throw wexception("save_to_png: could not create png info struct");
 	}
 
 	// Set jump for error
 	if (setjmp(png_jmpbuf(png_ptr))) {
 		png_destroy_write_struct(&png_ptr, &info_ptr);
-		throw wexception("save_surface_to_png: Error writing PNG!");
+		throw wexception("save_to_png: Error writing PNG!");
 	}
 
 	//  Set another write function. This is potentially dangerouse because the
@@ -112,10 +112,10 @@
 	// Fill info struct
 	png_set_IHDR(png_ptr,
 	             info_ptr,
-	             surface->width(),
-	             surface->height(),
+	             texture->width(),
+	             texture->height(),
 	             8,
-	             (color_type == COLOR_TYPE::RGB) ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA,
+	             (color_type == ColorType::RGB) ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA,
 	             PNG_INTERLACE_NONE,
 	             PNG_COMPRESSION_TYPE_DEFAULT,
 	             PNG_FILTER_TYPE_DEFAULT);
@@ -123,22 +123,22 @@
 	// Start writing
 	png_write_info(png_ptr, info_ptr);
 	{
-		const uint16_t surf_w = surface->width();
-		const uint16_t surf_h = surface->height();
-		const uint32_t row_size = (color_type == COLOR_TYPE::RGB) ? 3 * surf_w : 4 * surf_w;
+		const uint16_t surf_w = texture->width();
+		const uint16_t surf_h = texture->height();
+		const uint32_t row_size = (color_type == ColorType::RGB) ? 3 * surf_w : 4 * surf_w;
 
 		std::unique_ptr<png_byte[]> row(new png_byte[row_size]);
 
 		// Write each row
-		const SDL_PixelFormat& fmt = surface->format();
-		surface->lock(Surface::Lock_Normal);
+		const SDL_PixelFormat& fmt = texture->format();
+		texture->lock();
 
 		// Write each row
 		RGBAColor color;
-		if (color_type == COLOR_TYPE::RGB) {
+		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, surface->get_pixel(x, y));
+					color.set(fmt, 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, surface->get_pixel(x, y));
+					color.set(fmt, texture->get_pixel(x, y));
 					row[4 * x] = color.r;
 					row[4 * x + 1] = color.g;
 					row[4 * x + 2] = color.b;
@@ -157,7 +157,7 @@
 				png_write_row(png_ptr, row.get());
 			}
 		}
-		surface->unlock(Surface::Unlock_NoChange);
+		texture->unlock(Texture::Unlock_NoChange);
 	}
 	// End write
 	png_write_end(png_ptr, info_ptr);

=== modified file 'src/graphic/image_io.h'
--- src/graphic/image_io.h	2014-12-08 05:59:45 +0000
+++ src/graphic/image_io.h	2014-12-08 05:59:46 +0000
@@ -28,9 +28,7 @@
 class FileSystem;
 class Texture;
 class StreamWrite;
-class Surface;
 struct SDL_Surface;
-enum class COLOR_TYPE {RGB, RGBA};
 
 class ImageNotFound : public WException {
 public:
@@ -51,7 +49,8 @@
 /// Loads the image 'fn' from 'fs' into an SDL_Surface. Caller must SDL_FreeSurface() the returned value.
 SDL_Surface* load_image_as_sdl_surface(const std::string& fn, FileSystem* fs = nullptr);
 
-/// Saves the 'surface' to 'sw' as a PNG.
-bool save_surface_to_png(Surface* surface, StreamWrite* sw, COLOR_TYPE color_type);
+/// Saves the 'texture' to 'sw' as a PNG.
+enum class ColorType {RGB, RGBA};
+bool save_to_png(Texture* texture, StreamWrite* sw, ColorType color_type);
 
 #endif  // end of include guard: WL_GRAPHIC_IMAGE_IO_H

=== renamed file 'src/graphic/in_memory_image.cc' => 'src/graphic/in_memory_image.cc.THIS'
--- src/graphic/in_memory_image.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/in_memory_image.cc.THIS	2014-12-08 05:59:46 +0000
@@ -38,25 +38,24 @@
 // or prepare for core dumps.
 class InMemoryImage : public Image {
 public:
-<<<<<<< TREE
 	InMemoryImage(const string& ghash, Texture* init_texture) :
 		hash_(ghash), texture_(init_texture) {}
-=======
-	InMemoryImage(Texture* texture) :
-		texture_(texture) {}
->>>>>>> MERGE-SOURCE
 	virtual ~InMemoryImage() {
 	}
 
 	// Implements Image.
 	uint16_t width() const override {return texture_->width();}
 	uint16_t height() const override {return texture_->height();}
+	// Note: hash will mostly be dummy values for this implementation. It should
+	// not wind up in ImageCache, otherwise the ownership question is not clear.
+	const string& hash() const override {return hash_;}
 	Texture* texture() const override {return texture_.get();}
 
 private:
+	const string hash_;
 	std::unique_ptr<Texture> texture_;
 };
 
-std::unique_ptr<const Image> new_in_memory_image(Texture* texture) {
-	return std::unique_ptr<const Image>(new InMemoryImage(texture));
+const Image* new_in_memory_image(const string& hash, Texture* texture) {
+	return new InMemoryImage(hash, texture);
 }

=== removed file 'src/graphic/in_memory_image.h'
--- src/graphic/in_memory_image.h	2014-12-08 05:59:45 +0000
+++ src/graphic/in_memory_image.h	1970-01-01 00:00:00 +0000
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2006-2013 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_IN_MEMORY_IMAGE_H
-#define WL_GRAPHIC_IN_MEMORY_IMAGE_H
-
-#include <memory>
-
-class Texture;
-class Image;
-
-std::unique_ptr<const Image> new_in_memory_image(Texture* texture);
-
-#endif  // end of include guard: WL_GRAPHIC_IN_MEMORY_IMAGE_H

=== modified file 'src/graphic/minimap_renderer.cc'
--- src/graphic/minimap_renderer.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/minimap_renderer.cc	2014-12-08 05:59:46 +0000
@@ -25,8 +25,6 @@
 #include "economy/flag.h"
 #include "economy/road.h"
 #include "graphic/graphic.h"
-#include "graphic/image.h"
-#include "graphic/in_memory_image.h"
 #include "graphic/texture.h"
 #include "logic/field.h"
 #include "logic/map.h"
@@ -260,7 +258,6 @@
                                       const Player* player,
                                       const Point& viewpoint,
                                       MiniMapLayer layers) {
-	// First create a temporary SDL Surface to draw the minimap.
 	// TODO(unknown): Currently the minimap is redrawn every frame. That is not really
 	//       necesary. The created texture could be cached and only redrawn two
 	//       or three times per second
@@ -271,12 +268,12 @@
 	Texture* texture = new Texture(map_w, map_h);
 	assert(texture->format().BytesPerPixel == sizeof(uint32_t));
 
-	texture->fill_rect(Rect(0, 0, texture->width(), texture->height()), RGBAColor(0, 0, 0, 255));
-	texture->lock(Surface::Lock_Normal);
+	fill_rect(Rect(0, 0, texture->width(), texture->height()), RGBAColor(0, 0, 0, 255), texture);
+	texture->lock();
 
 	draw_minimap_int(texture, egbase, player, viewpoint, layers);
 
-	texture->unlock(Surface::Unlock_Update);
+	texture->unlock(Texture::Unlock_Update);
 
 	return std::unique_ptr<Texture>(texture);
 }
@@ -308,7 +305,5 @@
 
 	// Render minimap
 	std::unique_ptr<Texture> texture(draw_minimap(egbase, player, viewpoint, layers));
-	std::unique_ptr<const Image> image(new_in_memory_image(texture.release()));
-	g_gr->save_png(image.get(), streamwrite);
-	image.reset();
+	g_gr->save_png(texture.get(), streamwrite);
 }

=== modified file 'src/graphic/minimap_renderer.h'
--- src/graphic/minimap_renderer.h	2014-11-26 09:36:46 +0000
+++ src/graphic/minimap_renderer.h	2014-12-08 05:59:46 +0000
@@ -56,7 +56,7 @@
 
 /// Render the minimap. If player is not nullptr, it renders from that player's
 /// point of view.
-/// \param viewpoint: top left corner in map coordinates
+/// \param viewpoint top left corner in map coordinates
 std::unique_ptr<Texture> draw_minimap
 	(const Widelands::EditorGameBase& egbase, const Widelands::Player* player,
 	 const Point& viewpoint, MiniMapLayer layers);

=== modified file 'src/graphic/rendertarget.cc'
--- src/graphic/rendertarget.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/rendertarget.cc	2014-12-08 05:59:46 +0000
@@ -127,10 +127,13 @@
 	(int32_t x1, int32_t y1, int32_t x2, int32_t y2,
 	 const RGBColor& color, uint8_t gwidth)
 {
-	m_surface->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);
+	::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);
 }
 
 /**
@@ -139,22 +142,23 @@
 void RenderTarget::draw_rect(const Rect& rect, const RGBColor& clr)
 {
 	Rect r(rect);
-	if (clip(r))
-		m_surface->draw_rect(r, clr);
+	if (clip(r)) {
+		::draw_rect(r, clr, m_surface);
+	}
 }
 
 void RenderTarget::fill_rect(const Rect& rect, const RGBAColor& clr)
 {
 	Rect r(rect);
 	if (clip(r))
-		m_surface->fill_rect(r, clr);
+		::fill_rect(r, clr, m_surface);
 }
 
 void RenderTarget::brighten_rect(const Rect& rect, int32_t factor)
 {
 	Rect r(rect);
 	if (clip(r))
-		m_surface->brighten_rect(r, factor);
+		::brighten_rect(r, factor, m_surface);
 }
 
 /**
@@ -171,11 +175,12 @@
 	Rect srcrc(Point(0, 0), image->width(), image->height());
 
 	if (to_surface_geometry(&destination_point, &srcrc)) {
-		m_surface->blit(Rect(destination_point.x, destination_point.y, srcrc.w, srcrc.h),
-		                image->texture(),
-		                srcrc,
-		                1.,
-		                blend_mode);
+		::blit(Rect(destination_point.x, destination_point.y, srcrc.w, srcrc.h),
+		     *image,
+		     srcrc,
+		     1.,
+		     blend_mode,
+		     m_surface);
 	}
 }
 
@@ -197,11 +202,12 @@
 
 	Point destination_point(dst);
 	if (to_surface_geometry(&destination_point, &srcrc))
-		m_surface->blit(Rect(destination_point.x, destination_point.y, srcrc.w, srcrc.h),
-		                image->texture(),
-		                srcrc,
-		                1.,
-		                blend_mode);
+		::blit(Rect(destination_point.x, destination_point.y, srcrc.w, srcrc.h),
+		       *image,
+		       srcrc,
+		       1.,
+		       blend_mode,
+		       m_surface);
 }
 
 void RenderTarget::blitrect_scale(const Rect& dst,
@@ -213,11 +219,12 @@
 	Point destination_point(dst.x, dst.y);
 	Rect srcrect(source_rect);
 	if (to_surface_geometry(&destination_point, &srcrect)) {
-		m_surface->blit(Rect(destination_point.x, destination_point.y, dst.w, dst.h),
-		                image->texture(),
-		                source_rect,
-							 opacity,
-		                blend_mode);
+		::blit(Rect(destination_point.x, destination_point.y, dst.w, dst.h),
+		       *image,
+		       source_rect,
+		       opacity,
+		       blend_mode,
+		       m_surface);
 	}
 }
 
@@ -228,11 +235,12 @@
 	Point destination_point(destination_rect.x, destination_rect.y);
 	Rect srcrect(source_rect);
 	if (to_surface_geometry(&destination_point, &srcrect)) {
-		m_surface->blit_monochrome(
+		blit_monochrome(
 		   Rect(destination_point.x, destination_point.y, destination_rect.w, destination_rect.h),
-		   image->texture(),
+		   *image,
 		   source_rect,
-		   blend);
+		   blend,
+		   m_surface);
 	}
 }
 
@@ -289,7 +297,7 @@
 					srcrc.w = r.w - tx;
 
 				const Rect dst_rect(r.x + tx, r.y + ty, srcrc.w, srcrc.h);
-				m_surface->blit(dst_rect, image->texture(), srcrc, 1., blend_mode);
+				::blit(dst_rect, *image, srcrc, 1., blend_mode, m_surface);
 
 				tx += srcrc.w;
 

=== modified file 'src/graphic/screen.cc'
--- src/graphic/screen.cc	2014-11-24 06:31:16 +0000
+++ src/graphic/screen.cc	2014-12-08 05:59:46 +0000
@@ -20,13 +20,13 @@
 
 #include <algorithm>
 #include <cassert>
+#include <memory>
 
+#include "base/wexception.h"
 #include "graphic/gl/utils.h"
+#include "graphic/texture.h"
 
-Screen::Screen(uint16_t w, uint16_t h)
-{
-	m_w = w;
-	m_h = h;
+Screen::Screen(int w, int h) : m_w(w), m_h(h) {
 }
 
 void Screen::pixel_to_gl(float* x, float* y) const {
@@ -34,50 +34,46 @@
 	*y = 1. - (*y / m_h) * 2.;
 }
 
-/**
- * Swap order of rows in m_pixels, to compensate for the upside-down nature of the
- * OpenGL coordinate system.
- */
-void Screen::swap_rows()
-{
-	uint8_t * begin_row = m_pixels.get();
-	uint8_t * end_row = m_pixels.get() + (m_w * (m_h - 1) * 4);
-
+void Screen::setup_gl() {
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+int Screen::width() const {
+	return m_w;
+}
+
+int Screen::height() const {
+	return m_h;
+}
+
+std::unique_ptr<Texture> Screen::to_texture() const {
+	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 (uint16_t x = 0; x < m_w * 4; ++x)
+		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;
 	}
-}
-
-void Screen::lock(Surface::LockMode mode)
-{
-	assert(!m_pixels);
-
-	m_pixels.reset(new uint8_t[m_w * m_h * 4]);
-
-	if (mode == Lock_Normal) {
-		// TODO(unknown): terrain dither picture somehow leave the alpha
-		// channel with non-1 values, so it is cleared before
-		// accessing pixels.
-		glColorMask(false, false, false, true);
-		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-		glColorMask(true, true, true, true);
-		glReadPixels(0, 0, m_w, m_h, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels.get());
-		swap_rows();
-	}
-}
-
-void Screen::unlock(Surface::UnlockMode mode)
-{
-	assert(m_pixels);
-
-	if (mode == Unlock_Update) {
-		swap_rows();
-		glDrawPixels(m_w, m_h, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels.get());
-	}
-
-	m_pixels.reset(nullptr);
+
+	// 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
+	// 'pixels'.
+	SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(pixels.get(),
+	                                                m_w,
+	                                                m_h,
+	                                                32,
+	                                                m_w * 4,
+	                                                0x000000ff,
+	                                                0x0000ff00,
+	                                                0x00ff0000,
+	                                                0xff000000);
+
+	return std::unique_ptr<Texture>(new Texture(surface));
 }

=== modified file 'src/graphic/screen.h'
--- src/graphic/screen.h	2014-11-24 06:31:16 +0000
+++ src/graphic/screen.h	2014-12-08 05:59:46 +0000
@@ -19,24 +19,34 @@
 #ifndef WL_GRAPHIC_SCREEN_H
 #define WL_GRAPHIC_SCREEN_H
 
+#include <memory>
+
+#include "base/macros.h"
 #include "graphic/surface.h"
 
 /**
- * This surface represents the screen in OpenGL mode.
+ * The screen.
  */
 class Screen : public Surface {
 public:
-	Screen(uint16_t w, uint16_t h);
+	Screen(int w, int h);
 	virtual ~Screen() {}
 
-	/// Interface implementations
-	void lock(LockMode) override;
-	void unlock(UnlockMode) override;
+	// 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,
+	// so use with care.
+	std::unique_ptr<Texture> to_texture() const;
 
 private:
-	void pixel_to_gl(float* x, float* y) const override;
+	const int m_w, m_h;
 
-	void swap_rows();
+	DISALLOW_COPY_AND_ASSIGN(Screen);
 };
 
 #endif  // end of include guard: WL_GRAPHIC_SCREEN_H

=== modified file 'src/graphic/surface.cc'
--- src/graphic/surface.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/surface.cc	2014-12-08 05:59:46 +0000
@@ -35,148 +35,58 @@
 #include "graphic/texture.h"
 
 
-uint16_t Surface::width() const {
-	return m_w;
-}
-
-uint16_t Surface::height() const {
-	return m_h;
-}
-
-uint8_t * Surface::get_pixels() const
-{
-	return m_pixels.get();
-}
-
-uint32_t Surface::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 Surface::get_pitch() const {
-	return 4 * m_w;
-}
-
-const SDL_PixelFormat & Surface::format() const {
-	return Gl::gl_rgba_format();
-}
-
-
-void Surface::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;
-}
-
-FloatRect Surface::to_opengl(const Rect& rect, ConversionMode mode) {
+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;
-	pixel_to_gl(&x1, &y1);
+	surface.pixel_to_gl(&x1, &y1);
 	float x2 = rect.x + rect.w - delta;
 	float y2 = rect.y + rect.h - delta;
-	pixel_to_gl(&x2, &y2);
-
+	surface.pixel_to_gl(&x2, &y2);
 	return FloatRect(x1, y1, x2 - x1, y2 - y1);
 }
 
-void Surface::draw_rect(const Rect& rc, const RGBColor& clr)
-{
-	glViewport(0, 0, width(), height());
-	DrawRectProgram::instance().draw(to_opengl(rc, ConversionMode::kMidPoint), clr);
-}
-
-/**
- * Draws a filled rectangle
- */
-void Surface::fill_rect(const Rect& rc, const RGBAColor& clr) {
-	glViewport(0, 0, width(), height());
-
-	glBlendFunc(GL_ONE, GL_ZERO);
-
-	FillRectProgram::instance().draw(to_opengl(rc, ConversionMode::kExact), clr);
-
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-}
-
-/**
- * Change the brightness of the given rectangle
- */
-void Surface::brighten_rect(const Rect& rc, const int32_t factor)
-{
-	if (!factor)
-		return;
-
-	glViewport(0, 0, width(), 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(rc, ConversionMode::kExact), RGBAColor(delta, delta, delta, 0));
-
-	if (factor < 0) {
-		glBlendEquation(GL_FUNC_ADD);
-	}
-
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-}
-
-void Surface::draw_line
-	(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const RGBColor& color, uint8_t gwidth)
-{
-	glViewport(0, 0, width(), height());
-
-	float gl_x1 = x1 + 0.5;
-	float gl_y1 = y1 + 0.5;
-	pixel_to_gl(&gl_x1, &gl_y1);
-
-	float gl_x2 = x2 + 0.5;
-	float gl_y2 = y2 + 0.5;
-	pixel_to_gl(&gl_x2, &gl_y2);
-
-	DrawLineProgram::instance().draw(gl_x1, gl_y1, gl_x2, gl_y2, color, gwidth);
-}
-
 // 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);
 }
 
-void Surface::src_and_dst_rect_to_gl(const Texture* texture,
-               const Rect& dst_rect,
-               const Rect& src_rect,
-               FloatRect* gl_dst_rect,
-               FloatRect* gl_src_rect) {
+// 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) {
 	// 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& texture_coordinates = texture->texture_coordinates();
+	const FloatRect& texture_coordinates = image.texture_coordinates();
 
 	float x1 = src_rect.x;
 	float y1 = src_rect.y;
-	pixel_to_gl_texture(texture->width(), texture->height(), &x1, &y1);
+	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(texture->width(), texture->height(), &x2, &y2);
+	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;
 
@@ -185,42 +95,116 @@
 	gl_src_rect->w = x2 - x1;
 	gl_src_rect->h = y2 - y1;
 
-	*gl_dst_rect = to_opengl(dst_rect, ConversionMode::kExact);
-}
-
-void Surface::blit
-	(const Rect& dst_rect, const Texture* texture, const Rect& src_rect, float opacity, BlendMode blend_mode)
-{
-	glViewport(0, 0, width(), height());
-
-	FloatRect gl_dst_rect, gl_src_rect;
-	src_and_dst_rect_to_gl(texture, dst_rect, src_rect, &gl_dst_rect, &gl_src_rect);
-
-	VanillaBlitProgram::instance().draw(
-	   gl_dst_rect, gl_src_rect, texture->get_gl_texture(), opacity, blend_mode);
-}
-
-void Surface::blit_monochrome(const Rect& dst_rect,
-                             const Texture* texture,
-                             const Rect& src_rect,
-                             const RGBAColor& blend) {
-	glViewport(0, 0, width(), height());
-
-	FloatRect gl_dst_rect, gl_src_rect;
-	src_and_dst_rect_to_gl(texture, dst_rect, src_rect, &gl_dst_rect, &gl_src_rect);
+	*gl_dst_rect = to_opengl(surface, dst_rect, ConversionMode::kExact);
+}
+
+}  // 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)
+{
+	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);
+}
+
+void draw_line
+	(int x1, int y1, int x2, int y2, const RGBColor& color, int gwidth, Surface * surface)
+{
+	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, texture->get_gl_texture(), blend);
+	   gl_dst_rect, gl_src_rect, image.get_gl_texture(), blend);
 }
 
-void Surface::blit_blended(const Rect& dst_rect,
-                           const Texture* texture,
-                           const Texture* mask,
-                           const Rect& src_rect,
-                           const RGBColor& blend) {
+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(texture, dst_rect, src_rect, &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, texture->get_gl_texture(), mask->get_gl_texture(), blend);
+	   gl_dst_rect, gl_src_rect, image.get_gl_texture(), mask.get_gl_texture(), blend);
+}
+
+void draw_rect(const Rect& rc, const RGBColor& clr, Surface* surface) {
+	surface->setup_gl();
+	glViewport(0, 0, surface->width(), surface->height());
+	DrawRectProgram::instance().draw(to_opengl(*surface, rc, ConversionMode::kMidPoint), clr);
+}
+
+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);
 }

=== modified file 'src/graphic/surface.h'
--- src/graphic/surface.h	2014-12-08 05:59:45 +0000
+++ src/graphic/surface.h	2014-12-08 05:59:46 +0000
@@ -26,152 +26,62 @@
 #include "base/rect.h"
 #include "graphic/blend_mode.h"
 #include "graphic/color.h"
+#include "graphic/image.h"
 
 class Texture;
 
-/**
- * Interface to a basic surfaces that can be used as destination for blitting and drawing.
- * It also allows low level pixel access.
- */
+// Interface to a basic surfaces that can be used as destination for blitting
+// and drawing. It also allows low level pixel access.
 class Surface {
 public:
 	Surface() = default;
 	virtual ~Surface() {}
 
 	/// Dimensions.
-	uint16_t width() const;
-	uint16_t height() const;
-
-	/// This draws a part of another surface to this surface
-	virtual void blit(const Rect& dst,
-	                  const Texture*,
-	                  const Rect& srcrc,
-							const float opacity,
-	                  BlendMode blend_mode);
-
-	/// This draws a grayed out version. See MonochromeBlitProgram.
-	virtual void blit_monochrome(const Rect& dst,
-	                  const Texture*,
-	                  const Rect& srcrc,
-	                  const RGBAColor& multiplier);
-
-	/// This draws a playercolor blended image. See BlendedBlitProgram.
-	virtual void blit_blended(const Rect& dst,
-	                  const Texture* image,
-							const Texture* mask,
-	                  const Rect& srcrc,
-	                  const RGBColor& blend);
-
-	/// Draws a filled rect to the surface. No blending takes place, the values
-	// in the target are just replaced (i.e. / BlendMode would be BlendMode::Copy).
-	virtual void fill_rect(const Rect&, const RGBAColor&);
-
-	/// Draws a rect (frame only) to the surface.
-	virtual void draw_rect(const Rect&, const RGBColor&);
-
-	/// draw a line to the surface
-	virtual void draw_line
-		(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const RGBColor& color, uint8_t width = 1);
-
-	/// makes a rectangle on the surface brighter (or darker).
-	virtual void brighten_rect(const Rect&, int32_t factor);
-
-	/// The functions below are for direct pixel access. This should be used
-	/// only very sparingly as / it is potentially expensive (especially for
-	/// OpenGL). At the moment, only the code inside graphic / is actually using
-	/// this.
-	enum LockMode {
-		/**
-		 * Normal mode preserves pre-existing pixel data so that it can
-		 * be read or modified.
-		 */
-		Lock_Normal = 0,
-
-		/**
-		 * Discard mode discards pre-existing pixel data. All pixels
-		 * will be undefined unless they are re-written.
-		 */
-		Lock_Discard
-	};
-
-	enum UnlockMode {
-		/**
-		 * Update mode will ensure that any changes in the pixel data
-		 * will appear in subsequent operations.
-		 */
-		Unlock_Update = 0,
-
-		/**
-		 * NoChange mode indicates that the caller changed no pixel data.
-		 *
-		 * \note If the caller did change pixel data but specifies NoChange
-		 * mode, the results are undefined.
-		 */
-		Unlock_NoChange
-	};
-
-	/// This returns the pixel format for direct pixel access.
-	const SDL_PixelFormat & format() const;
-
-	/**
-	 * \return Pitch of the raw pixel data, i.e. the number of bytes
-	 * contained in each image row. This can be strictly larger than
-	 * bytes per pixel times the width.
-	 */
-	uint16_t get_pitch() const;
-
-	/**
-	 * \return Pointer to the raw pixel data.
-	 *
-	 * \warning 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.
-	 *
-	 * \note Lock/Unlock pairs cannot be nested.
-	 */
-	virtual void lock(LockMode) = 0;
-	virtual void unlock(UnlockMode) = 0;
-
-	uint32_t get_pixel(uint16_t x, uint16_t y);
-	void set_pixel(uint16_t x, uint16_t y, uint32_t clr);
+	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;
 
-protected:
-	// 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 Rect& rect, ConversionMode mode);
-
-	// 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 Texture* texture,
-	                            const Rect& dst,
-	                            const Rect& srcrc,
-	                            FloatRect* gl_dst_rect,
-	                            FloatRect* gl_src_rect);
-
-	/// Logical width and height of the surface
-	uint16_t m_w, m_h;
-
-	/// Pixel data, while the texture is locked
-	std::unique_ptr<uint8_t[]> m_pixels;
+	// Setups OpenGL to render to this surface.
+	virtual void setup_gl() = 0;
 
 private:
 	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/CMakeLists.txt'
--- src/graphic/text/CMakeLists.txt	2014-12-03 19:14:07 +0000
+++ src/graphic/text/CMakeLists.txt	2014-12-08 05:59:46 +0000
@@ -21,7 +21,7 @@
     base_exceptions
     base_geometry
     graphic_color
-    graphic_image
+    graphic_image_cache
     graphic_image_io
     graphic_surface
     io_fileread

=== modified file 'src/graphic/text/rt_render.cc'
--- src/graphic/text/rt_render.cc	2014-12-04 09:00:20 +0000
+++ src/graphic/text/rt_render.cc	2014-12-08 05:59:46 +0000
@@ -333,11 +333,12 @@
 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());
-	rv->blit(Rect(0, 0, img.width(), img.height()),
-	         &img,
-	         Rect(0, 0, img.width(), img.height()),
-	         1.,
-	         BlendMode::Copy);
+	blit(Rect(0, 0, img.width(), img.height()),
+	     img,
+	     Rect(0, 0, img.width(), img.height()),
+	     1.,
+	     BlendMode::Copy,
+	     rv);
 	return rv;
 }
 
@@ -365,7 +366,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);
-		rv->blit(Rect(curx, 0, srcrect.w, srcrect.h), &t, srcrect, 1., BlendMode::Copy);
+		blit(Rect(curx, 0, srcrect.w, srcrect.h), t, srcrect, 1., BlendMode::Copy, rv);
 	}
 	return rv;
 }
@@ -382,7 +383,7 @@
 	Texture* render(TextureCache* texture_cache) override {
 		if (m_show_spaces) {
 			Texture* rv = new Texture(m_w, m_h);
-			rv->fill_rect(Rect(0, 0, m_w, m_h), RGBAColor(0xff, 0, 0, 0xff));
+			fill_rect(Rect(0, 0, m_w, m_h), RGBAColor(0xff, 0, 0, 0xff), rv);
 			return rv;
 		}
 		return TextNode::render(texture_cache);
@@ -434,10 +435,10 @@
 				dst.y = 0;
 				srcrect.w = dst.w = min<int>(m_bg->width(), m_w - curx);
 				srcrect.h = dst.h = m_h;
-				rv->blit(dst, m_bg->texture(), srcrect, 1., BlendMode::Copy);
+				blit(dst, *m_bg, srcrect, 1., BlendMode::Copy, rv);
 			}
 		} else {
-			rv->fill_rect(Rect(0, 0, m_w, m_h), RGBAColor(255, 255, 255, 0));
+			fill_rect(Rect(0, 0, m_w, m_h), RGBAColor(255, 255, 255, 0), rv);
 		}
 		return rv;
 	}
@@ -474,13 +475,12 @@
 	uint16_t hotspot_y() override {return height();}
 	Texture* render(TextureCache* texture_cache) override {
 		Texture* rv = new Texture(width(), height());
-		rv->fill_rect(Rect(0, 0, rv->width(), rv->height()), RGBAColor(255, 255, 255, 0));
+		fill_rect(Rect(0, 0, rv->width(), rv->height()), RGBAColor(255, 255, 255, 0), rv);
 
 		// Draw Solid background Color
 		bool set_alpha = true;
 		if (m_bg_clr_set) {
-			Rect fill_rect(Point(m_margin.left, m_margin.top), m_w, m_h);
-			rv->fill_rect(fill_rect, m_bg_clr);
+			fill_rect(Rect(Point(m_margin.left, m_margin.top), m_w, m_h), m_bg_clr, rv);
 			set_alpha = false;
 		}
 
@@ -495,7 +495,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);
-					rv->blit(dst, m_bg_img->texture(), src, 1., BlendMode::Copy);
+					blit(dst, *m_bg_img, src, 1., BlendMode::Copy, rv);
 				}
 			}
 			set_alpha = false;
@@ -510,7 +510,7 @@
 				                node_texture->height());
 				Rect src = Rect(0, 0, node_texture->width(), node_texture->height());
 
-				rv->blit(dst, node_texture, src, 1., set_alpha ? BlendMode::Copy : BlendMode::UseAlpha);
+				blit(dst, *node_texture, src, 1., set_alpha ? BlendMode::Copy : BlendMode::UseAlpha, rv);
 				delete node_texture;
 			}
 			delete n;
@@ -561,11 +561,11 @@
 
 Texture* ImgRenderNode::render(TextureCache* /* texture_cache */) {
 	Texture* rv = new Texture(m_image.width(), m_image.height());
-	rv->blit(Rect(0, 0, m_image.width(), m_image.height()),
-	         m_image.texture(),
+	blit(Rect(0, 0, m_image.width(), m_image.height()),
+	         m_image,
 	         Rect(0, 0, m_image.width(), m_image.height()),
 				1.,
-	         BlendMode::Copy);
+	         BlendMode::Copy, rv);
 	return rv;
 }
 // End: Helper Stuff

=== modified file 'src/graphic/text/test/CMakeLists.txt'
--- src/graphic/text/test/CMakeLists.txt	2014-11-23 10:13:14 +0000
+++ src/graphic/text/test/CMakeLists.txt	2014-12-08 05:59:46 +0000
@@ -21,7 +21,7 @@
     render.cc
     render.h
   DEPENDS
-    graphic_image
+    graphic_image_cache
     graphic_surface
     graphic_text
     io_filesystem

=== modified file 'src/graphic/text/test/render.cc'
--- src/graphic/text/test/render.cc	2014-11-24 07:10:03 +0000
+++ src/graphic/text/test/render.cc	2014-12-08 05:59:46 +0000
@@ -36,7 +36,7 @@
 	g_fs->add_file_system(&FileSystem::create(RICHTEXT_DATA_DIR));
 
 	texture_cache_.reset(create_texture_cache(500 << 20));  // 500 MB
-	image_cache_.reset(new ImageCache(texture_cache_.get()));
+	image_cache_.reset(new ImageCache());
 	renderer_.reset(new RT::Renderer(image_cache_.get(), texture_cache_.get()));
 }
 

=== modified file 'src/graphic/text/test/render_richtext.cc'
--- src/graphic/text/test/render_richtext.cc	2014-11-30 13:18:04 +0000
+++ src/graphic/text/test/render_richtext.cc	2014-12-08 05:59:46 +0000
@@ -140,7 +140,7 @@
 
 		std::unique_ptr<FileSystem> fs(&FileSystem::create("."));
 		std::unique_ptr<StreamWrite> sw(fs->open_stream_write(outname));
-		if (!save_surface_to_png(texture.get(), sw.get(), COLOR_TYPE::RGBA)) {
+		if (!save_to_png(texture.get(), sw.get(), ColorType::RGBA)) {
 			std::cout << "Could not encode PNG." << std::endl;
 		}
 	} catch (RT::Exception& e) {

=== modified file 'src/graphic/texture.cc'
--- src/graphic/texture.cc	2014-12-08 05:59:45 +0000
+++ src/graphic/texture.cc	2014-12-08 05:59:46 +0000
@@ -61,14 +61,6 @@
 	return (fmt.Bmask == 0x000000ff && fmt.Gmask == 0x0000ff00 && fmt.Rmask == 0x00ff0000);
 }
 
-inline void setup_gl(const GLuint texture) {
-	glBindFramebuffer(GL_FRAMEBUFFER, GlFramebuffer::instance().id());
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
-}
-
-inline void reset_gl() {
-	glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
 
 }  // namespace
 
@@ -95,9 +87,8 @@
 	// use freetype directly we might be able to avoid that.
 	uint8_t bpp = surface->format->BytesPerPixel;
 
-	if (surface->format->palette || m_w != static_cast<uint32_t>(surface->w) ||
-	    m_h != static_cast<uint32_t>(surface->h) || (bpp != 3 && bpp != 4) ||
-	    is_bgr_surface(*surface->format)) {
+	if (surface->format->palette || m_w != surface->w || m_h != surface->h ||
+	    (bpp != 3 && bpp != 4) || is_bgr_surface(*surface->format)) {
 		SDL_Surface* converted = empty_sdl_surface(m_w, m_h);
 		assert(converted);
 		SDL_SetSurfaceAlphaMod(converted,  SDL_ALPHA_OPAQUE);
@@ -146,6 +137,22 @@
 	}
 }
 
+int Texture::width() const {
+	return m_w;
+}
+
+int Texture::height() const {
+	return m_h;
+}
+
+int Texture::get_gl_texture() const {
+	return m_texture;
+}
+
+const FloatRect& Texture::texture_coordinates() const {
+	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.;
@@ -175,7 +182,7 @@
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 }
 
-void Texture::lock(LockMode mode) {
+void Texture::lock() {
 	if (m_w <= 0 || m_h <= 0) {
 		return;
 	}
@@ -189,11 +196,9 @@
 
 	m_pixels.reset(new uint8_t[m_w * m_h * 4]);
 
-	if (mode == Lock_Normal) {
-		glBindTexture(GL_TEXTURE_2D, m_texture);
-		glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels.get());
-		glBindTexture(GL_TEXTURE_2D, 0);
-	}
+	glBindTexture(GL_TEXTURE_2D, m_texture);
+	glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels.get());
+	glBindTexture(GL_TEXTURE_2D, 0);
 }
 
 void Texture::unlock(UnlockMode mode) {
@@ -213,92 +218,40 @@
 	m_pixels.reset(nullptr);
 }
 
-void Texture::draw_rect(const Rect& rectangle, const RGBColor& clr)
-{
-	if (m_w <= 0 || m_h <= 0) {
-		return;
-	}
-	setup_gl(m_texture);
-	Surface::draw_rect(rectangle, clr);
-	reset_gl();
-}
-
-
-/**
- * Draws a filled rectangle
- */
-void Texture::fill_rect(const Rect& rectangle, const RGBAColor& clr)
-{
-	if (m_w <= 0 || m_h <= 0) {
-		return;
-	}
-
-	setup_gl(m_texture);
-	Surface::fill_rect(rectangle, clr);
-	reset_gl();
-}
-
-/**
- * Change the brightness of the given rectangle
- */
-void Texture::brighten_rect(const Rect& rectangle, const int32_t factor)
-{
-	if (m_w <= 0 || m_h <= 0) {
-		return;
-	}
-
-	setup_gl(m_texture);
-	Surface::brighten_rect(rectangle, factor);
-	reset_gl();
-}
-
-void Texture::draw_line
-	(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const RGBColor& color, uint8_t gwidth)
-{
-	if (m_w <= 0 || m_h <= 0) {
-		return;
-	}
-
-	setup_gl(m_texture);
-	Surface::draw_line(x1, y1, x2, y2, color, gwidth);
-	reset_gl();
-}
-
-void Texture::blit
-	(const Rect& dst, const Texture* src, const Rect& srcrc, float opacity, BlendMode blend_mode)
-{
-	if (m_w <= 0 || m_h <= 0) {
-		return;
-	}
-
-	setup_gl(m_texture);
-	Surface::blit(dst, src, srcrc, opacity, blend_mode);
-	reset_gl();
-}
-
-void Texture::blit_monochrome(const Rect& dst,
-                        const Texture* src,
-                        const Rect& srcrc,
-                        const RGBAColor& blend) {
-	if (m_w <= 0 || m_h <= 0) {
-		return;
-	}
-
-	setup_gl(m_texture);
-	Surface::blit_monochrome(dst, src, srcrc, blend);
-	reset_gl();
-}
-
-void Texture::blit_blended(const Rect& dst,
-                           const Texture* image,
-                           const Texture* mask,
-                           const Rect& srcrc,
-                           const RGBColor& blend) {
-	if (m_w <= 0 || m_h <= 0) {
-		return;
-	}
-
-	setup_gl(m_texture);
-	Surface::blit_blended(dst, image, mask, srcrc, blend);
-	reset_gl();
+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;
+}
+
+
+void Texture::setup_gl() {
+	glBindFramebuffer(GL_FRAMEBUFFER, GlFramebuffer::instance().id());
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0);
 }

=== modified file 'src/graphic/texture.h'
--- src/graphic/texture.h	2014-12-08 05:59:45 +0000
+++ src/graphic/texture.h	2014-12-08 05:59:46 +0000
@@ -19,13 +19,15 @@
 #ifndef WL_GRAPHIC_TEXTURE_H
 #define WL_GRAPHIC_TEXTURE_H
 
+#include <memory>
+
 #include "base/rect.h"
 #include "graphic/gl/system_headers.h"
 #include "graphic/surface.h"
 
 struct SDL_Surface;
 
-class Texture : public Surface {
+class Texture : public Surface, public Image {
 public:
 	// Create a new surface from an SDL_Surface. If intensity is true, an GL_INTENSITY texture
 	// is created. Ownership is taken.
@@ -41,46 +43,59 @@
 
 	virtual ~Texture();
 
-	/// Interface implementation
-	//@{
-	void lock(LockMode) override;
-	void unlock(UnlockMode) override;
-
-	// Note: the following functions are reimplemented here though they
-	// basically only call the functions in Surface wrapped in calls to
-	// setup_gl(), reset_gl(). The same functionality can be achieved by making
-	// those two functions virtual and calling them in Surface. However,
-	// especially for blit which is called very often and mostly on the screen,
-	// this costs two virtual function calls which makes a notable difference in
-	// profiles.
-	void fill_rect(const Rect&, const RGBAColor&) override;
-	void draw_rect(const Rect&, const RGBColor&) override;
-	void brighten_rect(const Rect&, int32_t factor) override;
-	virtual void draw_line
-		(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const RGBColor&, uint8_t width) override;
-	void blit(const Rect& dstretc,
-	          const Texture*,
-	          const Rect& srcrc,
-				 float opacity,
-	          BlendMode blend_mode) override;
-	void
-	blit_monochrome(const Rect& dst, const Texture*, const Rect& srcrc, const RGBAColor& blend) override;
-	void blit_blended(const Rect& dst,
-	                  const Texture* image,
-	                  const Texture* mask,
-	                  const Rect& srcrc,
-	                  const RGBColor& blend) override;
-
-	GLuint get_gl_texture() const {return m_texture;}
-
-	const FloatRect& texture_coordinates() const {
-		return m_texture_coordinates;
-	}
+	// 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;
+	const FloatRect& texture_coordinates() const override;
+
+	enum UnlockMode {
+		/**
+		 * Update mode will ensure that any changes in the pixel data
+		 * will appear in subsequent operations.
+		 */
+		Unlock_Update = 0,
+
+		/**
+		 * NoChange mode indicates that the caller changed no pixel data.
+		 *
+		 * \note If the caller did change pixel data but specifies NoChange
+		 * mode, the results are undefined.
+		 */
+		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);
+
+	// Sets the pixel to the 'clr'.
+	void set_pixel(uint16_t x, uint16_t y, uint32_t clr);
 
 private:
-	void pixel_to_gl(float* x, float* y) const override;
 	void init(uint16_t w, uint16_t h);
 
+	// Width and height.
+	int m_w, m_h;
+
 	// True if we own the texture, i.e. if we need to delete it.
 	bool m_owns_texture;
 
@@ -88,6 +103,11 @@
 	FloatRect m_texture_coordinates;
 
 	GLuint m_texture;
+
+	/// Pixel data, while the texture is locked
+	std::unique_ptr<uint8_t[]> m_pixels;
+
+	DISALLOW_COPY_AND_ASSIGN(Texture);
 };
 
 #endif  // end of include guard: WL_GRAPHIC_TEXTURE_H

=== modified file 'src/graphic/texture_atlas.cc'
--- src/graphic/texture_atlas.cc	2014-12-04 21:02:35 +0000
+++ src/graphic/texture_atlas.cc	2014-12-08 05:59:46 +0000
@@ -133,7 +133,7 @@
 	}
 
 	std::unique_ptr<Texture> packed_texture(new Texture(root->r.w, root->r.h));
-	packed_texture->fill_rect(Rect(0, 0, root->r.w, root->r.h), RGBAColor(0, 0, 0, 0));
+	fill_rect(Rect(0, 0, root->r.w, root->r.h), RGBAColor(0, 0, 0, 0), packed_texture.get());
 
 	// 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,12 +141,12 @@
 	});
 
 	for (Block& block : blocks_) {
-		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);
+		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());
 
 		textures->emplace_back(new Texture(
 		   packed_texture->get_gl_texture(),

=== modified file 'src/logic/map_info.cc'
--- src/logic/map_info.cc	2014-11-30 13:14:18 +0000
+++ src/logic/map_info.cc	2014-12-08 05:59:46 +0000
@@ -93,7 +93,7 @@
 		// Write minimap
 		{
 			FileWrite fw;
-			save_surface_to_png(minimap.get(), &fw, COLOR_TYPE::RGBA);
+			save_to_png(minimap.get(), &fw, ColorType::RGBA);
 			fw.write(*in_out_filesystem, (map_file + ".png").c_str());
 		}
 

=== modified file 'src/map_io/map_extradata_packet.cc'
--- src/map_io/map_extradata_packet.cc	2014-12-08 05:59:45 +0000
+++ src/map_io/map_extradata_packet.cc	2014-12-08 05:59:46 +0000
@@ -21,7 +21,6 @@
 
 #include "graphic/graphic.h"
 #include "graphic/image_io.h"
-#include "graphic/in_memory_image.h"
 #include "graphic/texture.h"
 #include "io/fileread.h"
 #include "io/filewrite.h"
@@ -59,8 +58,7 @@
 					const std::string hash = std::string("map:") + FileSystem::fs_filename(pname->c_str());
 					const Image* image = nullptr;
 					if (!g_gr->images().has(hash)) {
-						image = g_gr->images().insert(
-						   hash, new_in_memory_image(load_image(*pname, &fs).release()));
+						image = g_gr->images().insert(hash, load_image(*pname, &fs));
 					} else {
 						image = g_gr->images().get(hash);
 					}

=== modified file 'src/map_io/map_saver.cc'
--- src/map_io/map_saver.cc	2014-11-30 09:17:50 +0000
+++ src/map_io/map_saver.cc	2014-12-08 05:59:46 +0000
@@ -220,7 +220,7 @@
 		std::unique_ptr<Texture> minimap(
 		   draw_minimap(m_egbase, nullptr, Point(0, 0), MiniMapLayer::Terrain));
 		FileWrite fw;
-		save_surface_to_png(minimap.get(), &fw, COLOR_TYPE::RGBA);
+		save_to_png(minimap.get(), &fw, ColorType::RGBA);
 		fw.write(m_fs, "minimap.png");
 	}
 }

=== modified file 'src/ui_basic/CMakeLists.txt'
--- src/ui_basic/CMakeLists.txt	2014-11-28 16:40:55 +0000
+++ src/ui_basic/CMakeLists.txt	2014-12-08 05:59:46 +0000
@@ -58,7 +58,7 @@
     base_macros
     graphic
     graphic_color
-    graphic_image
+    graphic_surface
     io_filesystem
     # TODO(sirver): should not depend on logic
     logic

=== modified file 'src/ui_fsmenu/CMakeLists.txt'
--- src/ui_fsmenu/CMakeLists.txt	2014-11-12 13:13:25 +0000
+++ src/ui_fsmenu/CMakeLists.txt	2014-12-08 05:59:46 +0000
@@ -45,7 +45,6 @@
     build_info
     game_io
     graphic
-    graphic_image
     graphic_image_io
     graphic_surface
     helper

=== modified file 'src/ui_fsmenu/loadgame.cc'
--- src/ui_fsmenu/loadgame.cc	2014-12-08 05:59:45 +0000
+++ src/ui_fsmenu/loadgame.cc	2014-12-08 05:59:46 +0000
@@ -34,7 +34,6 @@
 #include "game_io/game_preload_packet.h"
 #include "graphic/graphic.h"
 #include "graphic/image_io.h"
-#include "graphic/in_memory_image.h"
 #include "graphic/texture.h"
 #include "helper.h"
 #include "io/filesystem/layered_filesystem.h"
@@ -316,12 +315,9 @@
 			if (!minimap_path.empty()) {
 				try {
 					// Load the image
-					std::unique_ptr<Texture> texture(
-								load_image(
-									minimap_path,
-									std::unique_ptr<FileSystem>(g_fs->make_sub_file_system(gamedata.filename)).get()));
-
-					m_minimap_image = new_in_memory_image(texture.release());
+					m_minimap_image = load_image(
+					   minimap_path,
+					   std::unique_ptr<FileSystem>(g_fs->make_sub_file_system(gamedata.filename)).get());
 
 					// Scale it
 					double scale = double(m_minimap_w) / m_minimap_image->width();

=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt	2014-12-04 20:54:13 +0000
+++ src/wui/CMakeLists.txt	2014-12-08 05:59:46 +0000
@@ -158,7 +158,6 @@
     graphic
     graphic_color
     graphic_game_renderer
-    graphic_image
     graphic_minimap_renderer
     graphic_surface
     io_fileread

=== modified file 'src/wui/minimap.cc'
--- src/wui/minimap.cc	2014-12-08 05:59:45 +0000
+++ src/wui/minimap.cc	2014-12-08 05:59:46 +0000
@@ -23,7 +23,6 @@
 
 #include "base/i18n.h"
 #include "graphic/graphic.h"
-#include "graphic/in_memory_image.h"
 #include "graphic/minimap_renderer.h"
 #include "graphic/rendertarget.h"
 #include "graphic/texture.h"
@@ -70,10 +69,7 @@
 	                   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));
-	// Give ownership of the texture to the new image
-	std::unique_ptr<const Image> im(new_in_memory_image(texture.release()));
-	dst.blit(Point(), im.get());
-	im.reset();
+	dst.blit(Point(), texture.get());
 }
 
 


Follow ups