← Back to team overview

widelands-dev team mailing list archive

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

 

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

Commit message:
- Write screenshot before swapping data into the backbuffer. Before this change, we used glReadPixels() which always reads the backbuffer, i.e. the last frame. Now we force a redraw when a screenshot is requested and save the screenshot from the freshly drawn backbuffer before it is swapped to the front.
- m_blub -> blub_ in class Graphic.
- removed Graphic::save_png, since it was a stateless wrapper for save_to_png().

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1302565 in widelands: "Screenshot shows wrong image when a game is chosen with the mouse pointer"
  https://bugs.launchpad.net/widelands/+bug/1302565

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

See commit message above.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/screenshot_from_backbuffer into lp:widelands.
=== modified file 'src/graphic/CMakeLists.txt'
--- src/graphic/CMakeLists.txt	2016-01-16 15:19:32 +0000
+++ src/graphic/CMakeLists.txt	2016-01-19 07:44:26 +0000
@@ -204,6 +204,7 @@
     base_macros
     economy
     graphic
+    graphic_image_io
     graphic_surface
     logic
     wui_mapview_pixelfunctions

=== modified file 'src/graphic/graphic.cc'
--- src/graphic/graphic.cc	2016-01-16 15:19:32 +0000
+++ src/graphic/graphic.cc	2016-01-19 07:44:26 +0000
@@ -70,9 +70,9 @@
  * Initialize the SDL video mode.
  */
 void Graphic::initialize(int window_mode_w, int window_mode_h, bool init_fullscreen) {
-	m_window_mode_width = window_mode_w;
-	m_window_mode_height = window_mode_h;
-	m_requires_update = true;
+	window_mode_width_ = window_mode_w;
+	window_mode_height_ = window_mode_h;
+	requires_update_ = true;
 
 	// Request an OpenGL 2 context with double buffering.
 	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
@@ -80,19 +80,19 @@
 	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 =
+	log("Graphics: Try to set Videomode %ux%u\n", window_mode_width_, window_mode_height_);
+	sdl_window_ =
 	   SDL_CreateWindow("Widelands Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
-	                    m_window_mode_width, m_window_mode_height, SDL_WINDOW_OPENGL);
+	                    window_mode_width_, window_mode_height_, SDL_WINDOW_OPENGL);
 
 	resolution_changed();
 	set_fullscreen(init_fullscreen);
 
-	SDL_SetWindowTitle(m_sdl_window, ("Widelands " + build_id() + '(' + build_type() + ')').c_str());
-	set_icon(m_sdl_window);
+	SDL_SetWindowTitle(sdl_window_, ("Widelands " + build_id() + '(' + build_type() + ')').c_str());
+	set_icon(sdl_window_);
 
-	m_glcontext = SDL_GL_CreateContext(m_sdl_window);
-	SDL_GL_MakeCurrent(m_sdl_window, m_glcontext);
+	gl_context_ = SDL_GL_CreateContext(sdl_window_);
+	SDL_GL_MakeCurrent(sdl_window_, gl_context_);
 
 #ifdef USE_GLBINDING
 	glbinding::Binding::initialize();
@@ -128,12 +128,12 @@
 
 	glClear(GL_COLOR_BUFFER_BIT);
 
-	SDL_GL_SwapWindow(m_sdl_window);
+	SDL_GL_SwapWindow(sdl_window_);
 
 	/* Information about the video capabilities. */
 	{
 		SDL_DisplayMode disp_mode;
-		SDL_GetWindowDisplayMode(m_sdl_window, &disp_mode);
+		SDL_GetWindowDisplayMode(sdl_window_, &disp_mode);
 		log("**** GRAPHICS REPORT ****\n"
 		    " VIDEO DRIVER %s\n"
 		    " pixel fmt %u\n"
@@ -156,13 +156,13 @@
 	if (UI::g_fh)
 		UI::g_fh->flush();
 
-	if (m_sdl_window) {
-		SDL_DestroyWindow(m_sdl_window);
-		m_sdl_window = nullptr;
+	if (sdl_window_) {
+		SDL_DestroyWindow(sdl_window_);
+		sdl_window_ = nullptr;
 	}
-	if (m_glcontext) {
-		SDL_GL_DeleteContext(m_glcontext);
-		m_glcontext = nullptr;
+	if (gl_context_) {
+		SDL_GL_DeleteContext(gl_context_);
+		gl_context_ = nullptr;
 	}
 }
 
@@ -183,21 +183,21 @@
 }
 
 void Graphic::change_resolution(int w, int h) {
-	m_window_mode_width = w;
-	m_window_mode_height = h;
+	window_mode_width_ = w;
+	window_mode_height_ = h;
 
 	if (!fullscreen()) {
-		SDL_SetWindowSize(m_sdl_window, w, h);
+		SDL_SetWindowSize(sdl_window_, w, h);
 		resolution_changed();
 	}
 }
 
 void Graphic::resolution_changed() {
 	int new_w, new_h;
-	SDL_GetWindowSize(m_sdl_window, &new_w, &new_h);
+	SDL_GetWindowSize(sdl_window_, &new_w, &new_h);
 
 	screen_.reset(new Screen(new_w, new_h));
-	m_rendertarget.reset(new RenderTarget(screen_.get()));
+	render_target_.reset(new RenderTarget(screen_.get()));
 
 	Notifications::publish(GraphicResolutionChanged{new_w, new_h});
 
@@ -209,13 +209,13 @@
 */
 RenderTarget * Graphic::get_render_target()
 {
-	m_rendertarget->reset();
-	return m_rendertarget.get();
+	render_target_->reset();
+	return render_target_.get();
 }
 
 bool Graphic::fullscreen()
 {
-	uint32_t flags = SDL_GetWindowFlags(m_sdl_window);
+	uint32_t flags = SDL_GetWindowFlags(sdl_window_);
 	return (flags & SDL_WINDOW_FULLSCREEN) || (flags & SDL_WINDOW_FULLSCREEN_DESKTOP);
 }
 
@@ -232,35 +232,33 @@
 	// whet fullscreen, we do it when in windowed mode.
 	if (value) {
 		SDL_DisplayMode display_mode;
-		SDL_GetDesktopDisplayMode(SDL_GetWindowDisplayIndex(m_sdl_window), &display_mode);
-		SDL_SetWindowSize(m_sdl_window, display_mode.w, display_mode.h);
+		SDL_GetDesktopDisplayMode(SDL_GetWindowDisplayIndex(sdl_window_), &display_mode);
+		SDL_SetWindowSize(sdl_window_, display_mode.w, display_mode.h);
 
-		SDL_SetWindowFullscreen(m_sdl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+		SDL_SetWindowFullscreen(sdl_window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
 	} else {
-		SDL_SetWindowFullscreen(m_sdl_window, 0);
+		SDL_SetWindowFullscreen(sdl_window_, 0);
 
 		// Next line does not work. See comment in refresh().
-		SDL_SetWindowSize(m_sdl_window, m_window_mode_width, m_window_mode_height);
+		SDL_SetWindowSize(sdl_window_, window_mode_width_, window_mode_height_);
 	}
 	resolution_changed();
 }
 
 
 void Graphic::update() {
-	m_requires_update = true;
+	requires_update_ = true;
 }
 
 /**
  * Returns true if parts of the screen have been marked for refreshing.
 */
 bool Graphic::need_update() const {
-	return m_requires_update;
+	return requires_update_;
 }
 
 /**
  * Bring the screen uptodate.
- *
- * \param force update whole screen
 */
 void Graphic::refresh()
 {
@@ -271,33 +269,33 @@
 	// refresh() when in window mode.
 	if (!fullscreen()) {
 		int true_width, true_height;
-		SDL_GetWindowSize(m_sdl_window, &true_width, &true_height);
-		if (true_width != m_window_mode_width || true_height != m_window_mode_height) {
-			SDL_SetWindowSize(m_sdl_window, m_window_mode_width, m_window_mode_height);
+		SDL_GetWindowSize(sdl_window_, &true_width, &true_height);
+		if (true_width != window_mode_width_ || true_height != window_mode_height_) {
+			SDL_SetWindowSize(sdl_window_, window_mode_width_, window_mode_height_);
 		}
 	}
 
-	SDL_GL_SwapWindow(m_sdl_window);
-	m_requires_update = false;
-}
-
-
-/**
- * Saves a pixel region to a png. This can be a file or part of a stream.
- *
- * @param surf The Surface to save
- * @param sw a StreamWrite where the png is written to
- */
-void Graphic::save_png(Texture* texture, StreamWrite * sw) const {
-	save_to_png(texture, sw, ColorType::RGBA);
+	// The backbuffer now contains the current frame. If we want a screenshot,
+	// we should better take it now, before this is swapped out to the
+	// frontbuffer and becomes inaccessible to us.
+	if (!screenshot_filename_.empty()) {
+		log("Save screenshot to %s\n", screenshot_filename_.c_str());
+		std::unique_ptr<StreamWrite> sw(g_fs->open_stream_write(screenshot_filename_));
+		save_to_png(screen_->to_texture().get(), sw.get(), ColorType::RGB);
+		screenshot_filename_.clear();
+	}
+
+	SDL_GL_SwapWindow(sdl_window_);
+	requires_update_ = false;
 }
 
 /**
  * Save a screenshot to the given file.
 */
-void Graphic::screenshot(const string& fname) const
+void Graphic::screenshot(const string& fname)
 {
-	log("Save screenshot to %s\n", fname.c_str());
-	std::unique_ptr<StreamWrite> sw(g_fs->open_stream_write(fname));
-	save_to_png(screen_->to_texture().get(), sw.get(), ColorType::RGB);
+	screenshot_filename_ = fname;
+
+	// Force a redraw of the screen soon.
+	update();
 }

=== modified file 'src/graphic/graphic.h'
--- src/graphic/graphic.h	2016-01-09 15:27:05 +0000
+++ src/graphic/graphic.h	2016-01-19 07:44:26 +0000
@@ -71,41 +71,48 @@
 	void update();
 	bool need_update() const;
 	void refresh();
-	SDL_Window* get_sdlwindow() {return m_sdl_window;}
+	SDL_Window* get_sdlwindow() {return sdl_window_;}
 
 	ImageCache& images() const {return *image_cache_.get();}
 	AnimationManager& animations() const {return *animation_manager_.get();}
 
-	void save_png(Texture*, StreamWrite*) const;
-
-	void screenshot(const std::string& fname) const;
+	// Requests a screenshot being taken on the next frame.
+	void screenshot(const std::string& fname);
 
 private:
 	// Called when the resolution (might) have changed.
 	void resolution_changed();
 
 	// The height & width of the window should we be in window mode.
-	int m_window_mode_width;
-	int m_window_mode_height;
+	int window_mode_width_;
+	int window_mode_height_;
 
 	/// This is the main screen Surface.
 	/// A RenderTarget for this can be retrieved with get_render_target()
 	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.
-	SDL_Window * m_sdl_window;
-	SDL_GLContext m_glcontext;
+	SDL_Window* sdl_window_;
+	SDL_GLContext gl_context_;
+
 	/// A RenderTarget for screen_. This is initialized during init()
-	std::unique_ptr<RenderTarget> m_rendertarget;
+	std::unique_ptr<RenderTarget> render_target_;
+
 	/// This marks the complete screen for updating.
-	bool m_requires_update;
+	bool requires_update_;
 
 	/// Non-volatile cache of independent images.
 	std::unique_ptr<ImageCache> image_cache_;
 
 	/// This holds all animations.
 	std::unique_ptr<AnimationManager> animation_manager_;
+
+	/// Screenshot filename. If a screenshot is requested, this will be set to
+	/// the requested filename. On the next frame the screenshot will be written
+	/// out and this will be clear()ed again.
+	std::string screenshot_filename_;
 };
 
 extern Graphic * g_gr;

=== modified file 'src/graphic/minimap_renderer.cc'
--- src/graphic/minimap_renderer.cc	2016-01-08 21:00:39 +0000
+++ src/graphic/minimap_renderer.cc	2016-01-19 07:44:26 +0000
@@ -25,6 +25,7 @@
 #include "economy/flag.h"
 #include "economy/road.h"
 #include "graphic/graphic.h"
+#include "graphic/image_io.h"
 #include "graphic/texture.h"
 #include "logic/field.h"
 #include "logic/map.h"
@@ -263,5 +264,5 @@
 
 	// Render minimap
 	std::unique_ptr<Texture> texture(draw_minimap(egbase, player, viewpoint, layers));
-	g_gr->save_png(texture.get(), streamwrite);
+	save_to_png(texture.get(), streamwrite, ColorType::RGBA);
 }


References