"Print Screen"-Key takes screenshots
authorChristoph Sommer <mail@christoph-sommer.de>
Mon, 9 Apr 2007 00:24:45 +0000 (00:24 +0000)
committerChristoph Sommer <mail@christoph-sommer.de>
Mon, 9 Apr 2007 00:24:45 +0000 (00:24 +0000)
SVN-Revision: 4972

src/mainloop.cpp
src/mainloop.hpp
src/video/drawing_context.cpp
src/video/drawing_context.hpp

index 07d8536..0a2daeb 100644 (file)
@@ -49,7 +49,7 @@ static const int MAX_FRAME_SKIP = 2;
 MainLoop* main_loop = NULL;
 
 MainLoop::MainLoop()
-  : speed(1.0), nextpop(false), nextpush(false), fps(0)
+  : speed(1.0), nextpop(false), nextpush(false), fps(0), screenshot_requested(false)
 {
   using namespace Scripting;
   TimeScheduler::instance = new TimeScheduler();
@@ -135,6 +135,11 @@ MainLoop::draw(DrawingContext& context)
   if(config->show_fps)
     draw_fps(context, fps);
 
+  // if a screenshot was requested, pass request on to drawing_context
+  if (screenshot_requested) {
+    context.take_screenshot();
+    screenshot_requested = false;
+  }
   context.do_drawing();
 
   /* Calculate frames per second */
@@ -177,6 +182,9 @@ MainLoop::process_events()
       config->use_fullscreen = !config->use_fullscreen;
       init_video();
     }
+    else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_PRINT) {
+      take_screenshot();
+    }
   }
 }
 
@@ -262,3 +270,10 @@ MainLoop::run()
     SDL_Delay(0);
   }
 }
+
+void 
+MainLoop::take_screenshot()
+{
+  screenshot_requested = true;
+}
+
index cb5bbed..244c63f 100644 (file)
@@ -39,6 +39,11 @@ public:
   void quit(ScreenFade* fade = NULL);
   void set_speed(float speed);
 
+  /**
+   * requests that a screenshot be taken after the next frame has been rendered
+   */
+  void take_screenshot();
+
   // push new screen on screen_stack
   void push_screen(Screen* screen, ScreenFade* fade = NULL);
   void set_screen_fade(ScreenFade* fade);
@@ -64,6 +69,7 @@ private:
   std::auto_ptr<Console> console;
   std::auto_ptr<ScreenFade> screen_fade;
   std::vector<Screen*> screen_stack;
+  bool screenshot_requested; /**< true if a screenshot should be taken after the next frame has been rendered */
 };
 
 extern MainLoop* main_loop;
index 89a92f2..60faef5 100644 (file)
@@ -24,6 +24,9 @@
 #include <iostream>
 #include <SDL_image.h>
 #include <GL/gl.h>
+#include <sstream>
+#include <iomanip>
+#include <physfs.h>
 
 #include "drawing_context.hpp"
 #include "surface.hpp"
@@ -106,7 +109,7 @@ static inline int next_po2(int val)
 }
 
 DrawingContext::DrawingContext()
-  : ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL)
+  : ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL), screenshot_requested(false)
 {
   screen = SDL_GetVideoSurface();
 
@@ -495,6 +498,12 @@ DrawingContext::do_drawing()
   obstack_init(&obst);
   assert_gl("drawing");
 
+  // if a screenshot was requested, take one
+  if (screenshot_requested) {
+    do_take_screenshot();
+    screenshot_requested = false;
+  }
+
   SDL_GL_SwapBuffers();
 }
 
@@ -626,3 +635,69 @@ DrawingContext::set_ambient_color( Color new_color )
 {
   ambient_color = new_color;
 }
+
+void 
+DrawingContext::do_take_screenshot()
+{
+  // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
+
+  // create surface to hold screenshot
+  #if SDL_BYTEORDER == SDL_BIG_ENDIAN
+  SDL_Surface* shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0);
+  #else
+  SDL_Surface* shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
+  #endif
+  if (!shot_surf) {
+    log_warning << "Could not create RGB Surface to contain screenshot" << std::endl;
+    return;
+  }
+
+  // read pixels into array
+  char* pixels = new char[3 * SCREEN_WIDTH * SCREEN_HEIGHT];
+  if (!pixels) {
+    log_warning << "Could not allocate memory to store screenshot" << std::endl;
+    SDL_FreeSurface(shot_surf);
+    return;
+  }
+  glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels);
+
+  // copy array line-by-line
+  for (int i = 0; i < SCREEN_HEIGHT; i++) {
+    char* src = pixels + (3 * SCREEN_WIDTH * (SCREEN_HEIGHT - i - 1));
+    char* dst = ((char*)shot_surf->pixels) + i * shot_surf->pitch;
+    memcpy(dst, src, 3 * SCREEN_WIDTH);
+  }
+
+  // free array
+  delete[](pixels);
+
+  // save screenshot
+  static const std::string writeDir = PHYSFS_getWriteDir();
+  static const std::string dirSep = PHYSFS_getDirSeparator();
+  static const std::string baseName = "screenshot";
+  static const std::string fileExt = ".bmp";
+  std::string fullFilename;
+  for (int num = 0; num < 1000; num++) {
+    std::ostringstream oss;
+    oss << baseName;
+    oss << std::setw(3) << std::setfill('0') << num;
+    oss << fileExt;
+    std::string fileName = oss.str();
+    fullFilename = writeDir + dirSep + fileName;
+    if (!PHYSFS_exists(fileName.c_str())) {
+      SDL_SaveBMP(shot_surf, fullFilename.c_str());
+      log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
+      SDL_FreeSurface(shot_surf);
+      return;
+    }
+  }
+  log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
+  SDL_FreeSurface(shot_surf);
+}
+
+void 
+DrawingContext::take_screenshot()
+{
+  screenshot_requested = true;
+}
+
index de882a7..b742b37 100644 (file)
@@ -138,6 +138,11 @@ public:
 
   void set_ambient_color( Color new_color );
 
+  /**
+   * requests that a screenshot be taken after the next frame has been rendered
+   */
+  void take_screenshot();
+
 private:
   class Transform
   {
@@ -174,6 +179,7 @@ private:
   void draw_filled_rect(const DrawingRequest& request) const;
   void draw_lightmap(const DrawingRequest& request) const;
   void get_light(const DrawingRequest& request) const;
+  void do_take_screenshot();
 
   DrawingRequests drawing_requests;
   DrawingRequests lightmap_requests;
@@ -190,6 +196,8 @@ private:
 
   /* obstack holding the memory of the drawing requests */
   struct obstack obst;
+
+  bool screenshot_requested; /**< true if a screenshot should be taken after the next frame has been rendered */
 };
 
 #endif