|
From: Lasse Kärkkäi. <tr...@us...> - 2010-01-02 08:11:24
|
Module: performous
Branch: master
Commit: 6b6563dc41cce558d1468f10fb707c7996464141
Author: Lasse Karkkainen <tro...@tr...>
Date: Sat Jan 2 05:55:19 2010 +0200
Screenshot into PNG format. Key bindings changed: now F12 or Print takes a shot.
---
game/image.hh | 41 +++++++++++++++++++++++++++++-
game/main.cc | 4 +-
game/video_driver.cc | 69 +++++++------------------------------------------
3 files changed, 52 insertions(+), 62 deletions(-)
diff --git a/game/image.hh b/game/image.hh
index 8e4ad7b..2002e3a 100644
--- a/game/image.hh
+++ b/game/image.hh
@@ -1,16 +1,26 @@
#pragma once
+#include "configuration.hh"
#include "surface.hh"
#include "util.hh"
#include <jpeglib.h>
#include <librsvg/rsvg.h>
#include <librsvg/rsvg-cairo.h>
#include <png.h>
+#include <fstream>
+
+struct Image {
+ std::vector<unsigned char> data;
+ unsigned w, h;
+};
namespace {
void readPngHelper(png_structp pngPtr, png_bytep data, png_size_t length) {
static_cast<std::istream*>(png_get_io_ptr(pngPtr))->read((char*)data, length);
}
+ void writePngHelper(png_structp pngPtr, png_bytep data, png_size_t length) {
+ static_cast<std::ostream*>(png_get_io_ptr(pngPtr))->write((char*)data, length);
+ }
}
template <typename T> void loadSVG(T& target, std::string const& filename) {
@@ -72,7 +82,7 @@ template <typename T> void loadPNG(T& target, std::string const& filename) {
rows.resize(h);
unsigned pos = 0;
for (unsigned y = 0; y < h; ++y) {
- rows[y] = (png_bytep)&image[pos];
+ rows[y] = reinterpret_cast<png_bytep>(&image[pos]);
pos += (w * channels + 3) & ~3; // Rows need to be word aligned
}
png_read_image(pngPtr, &rows[0]);
@@ -93,6 +103,7 @@ template <typename T> void loadJPEG(T& target, std::string const& filename) {
unsigned w = cinfo.output_width;
unsigned h = cinfo.output_height;
image.resize(w * h * 4);
+ unsigned stride = (w * 3 + 3) & ~3; // Number of bytes per row (word-aligned)
unsigned char* ptr = &image[0];
while (cinfo.output_scanline < h) {
jpeg_read_scanlines(&cinfo, &ptr, 1);
@@ -103,3 +114,31 @@ template <typename T> void loadJPEG(T& target, std::string const& filename) {
target.load(w, h, pix::RGB, &image[0], float(w)/h);
}
+static inline void writePNG(std::string const& filename, Image const& img) {
+ std::ofstream file(filename.c_str(), std::ios::binary);
+ png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!pngPtr) throw std::runtime_error("png_create_read_struct failed");
+ png_infop infoPtr = NULL;
+ struct Cleanup {
+ png_structpp pngPP;
+ png_infopp infoPP;
+ Cleanup(png_structp& pngP, png_infop& infoP): pngPP(&pngP), infoPP(&infoP) {}
+ ~Cleanup() { png_destroy_write_struct(pngPP, infoPP); }
+ } cleanup(pngPtr, infoPtr);
+ infoPtr = png_create_info_struct(pngPtr);
+ if (!infoPtr) throw std::runtime_error("png_create_info_struct failed");
+ std::vector<png_bytep> rows(img.h);
+ // There must be no C++ objects after the setjmp line! (they won't get properly destructed)
+ if (setjmp(png_jmpbuf(pngPtr))) throw std::runtime_error("Writing PNG failed");
+ png_set_write_fn(pngPtr, &file, writePngHelper, NULL);
+ png_set_IHDR(pngPtr, infoPtr, img.w, img.h, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ png_write_info(pngPtr, infoPtr);
+ unsigned stride = (img.w * 3 + 3) & ~3; // Number of bytes per row (word-aligned)
+ unsigned pos = img.h * stride;
+ for (unsigned y = 0; y < img.h; ++y) {
+ pos -= stride;
+ rows[y] = (png_bytep)(&img.data[pos]);
+ }
+ png_write_image(pngPtr, &rows[0]);
+}
+
diff --git a/game/main.cc b/game/main.cc
index d34d9e6..e58da6f 100644
--- a/game/main.cc
+++ b/game/main.cc
@@ -71,7 +71,7 @@ static void checkEvents_SDL(ScreenManager& sm, Window& window) {
config["graphic/fullscreen"].b() = !config["graphic/fullscreen"].b();
continue; // Already handled here...
}
- if (keypressed == SDLK_PRINT || keypressed == SDLK_WORLD_7) { // WORLD_7 = section sign (next to 1)
+ if (keypressed == SDLK_PRINT || keypressed == SDLK_F12) {
g_take_screenshot = true;
}
if (keypressed == SDLK_F4 && modifier & KMOD_ALT) {
@@ -163,9 +163,9 @@ void mainLoop(std::string const& songlist) {
unsigned frames = 0;
while (!sm.isFinished()) {
if( g_take_screenshot ) {
+ fs::path filename;
try {
window.screenshot();
- std::cout << ">>> Screenshot taken" << std::endl;
} catch (std::exception& e) {
std::cerr << "ERROR: " << e.what() << std::endl;
}
diff --git a/game/video_driver.cc b/game/video_driver.cc
index 19b6b90..5f6cc2d 100644
--- a/game/video_driver.cc
+++ b/game/video_driver.cc
@@ -3,16 +3,13 @@
#include "config.hh"
#include "glutil.hh"
#include "fs.hh"
+#include "image.hh"
#include "util.hh"
#include "joystick.hh"
+#include <boost/date_time.hpp>
#include <fstream>
#include <SDL.h>
-#ifdef _WIN32
-// for GetTempPathA
-#include <windows.h>
-#endif
-
namespace {
unsigned s_width;
unsigned s_height;
@@ -61,60 +58,14 @@ bool Window::getFullscreen() {
}
void Window::screenshot() {
- static unsigned int count = 0;
- unsigned width = m_fullscreen ? m_fsW : m_windowW;
- unsigned height = m_fullscreen ? m_fsH : m_windowH;
-
- std::vector<char> buffer(width*height*3);
- glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, &buffer[0]);
- std::ostringstream fnstr;
-#ifdef _WIN32
- char tmppath[256];
- GetTempPathA(256, tmppath); /* this includes a backslash at the end */
- fnstr << tmppath;
-#else
- fnstr << "/tmp/";
-#endif
- fnstr << "performous_screenshot_" << count;
-#if 1 // TODO: implement with libpng?
-//#ifdef USE_SDL_IMAGE
- /* Write the image out as an uncompressed tga. */
- fnstr << ".tga";
- std::ofstream tgaout(fnstr.str().c_str(), std::ios::binary);
- char tga_hdr_part1[] = {
- 0x00, /* no identification field */
- 0x00, /* no palette */
- 0x02, /* 24-bit RGB */
- 0x00, 0x00, 0x00, 0x00, 0x00, /* palette info (ignored) */
- 0x00, 0x00, /* x-origin */
- 0x00, 0x00, /* y-origin */
- };
- tgaout.write(tga_hdr_part1, sizeof(tga_hdr_part1));
- /* two 16-bit little endian words for width and height */
- tgaout << static_cast<char>(width & 0xff);
- tgaout << static_cast<char>(width >> 8);
- tgaout << static_cast<char>(height & 0xff);
- tgaout << static_cast<char>(height >> 8);
- tgaout << static_cast<char>(24); /* bits per pixel */
- tgaout << static_cast<char>(0x00); /* no special flags */
- for (unsigned i = 0; i < width*height*3; i += 3)
- std::swap(buffer[i], buffer[i+2]); /* fix the channel order */
- tgaout.write(&buffer[0], width*height*3); /* dump the image data */
- tgaout.close();
-#else
- fnstr << ".png";
- Magick::Blob blob( &buffer[0], width*height*3);
- Magick::Image image;
- char geometry[16];
- sprintf(geometry,"%dx%d",width,height);
- image.size(geometry);
- image.depth(8);
- image.magick( "RGB" );
- image.read(blob);
- image.flip();
- image.write(fnstr.str().c_str());
-#endif
- count++;
+ Image img;
+ img.w = m_fullscreen ? m_fsW : m_windowW;
+ img.h = m_fullscreen ? m_fsH : m_windowH;
+ img.data.resize(((img.w + 3) & ~3) * img.h * 3);
+ glReadPixels(0, 0, img.w, img.h, GL_RGB, GL_UNSIGNED_BYTE, &img.data[0]);
+ fs::path filename = getHomeDir() / ("Performous_" + to_iso_string(boost::posix_time::second_clock::local_time()) + ".png");
+ writePNG(filename.string(), img);
+ std::cout << ">>> Screenshot taken: " << filename << " (" << img.w << "x" << img.h << ")" << std::endl;
}
|