save indexed screenshot to directory
This commit is contained in:
parent
ebd8f34266
commit
1dbb2a2e1d
|
@ -1,6 +1,10 @@
|
|||
// reset, pause, auto reset, analog d-pad, gamepad config, any key, confirm exit game
|
||||
/***
|
||||
|
||||
// sweaty gamer hands oily snacks and bad hygiene
|
||||
reset, pause, auto reset, analog d-pad, gamepad config, any key, confirm exit
|
||||
game, screen wipes
|
||||
:) SWEATY HANDS :) OILY SNACKS :) AND BAD HYGIENE :)
|
||||
|
||||
***/
|
||||
|
||||
#include "Demo.hpp"
|
||||
|
||||
|
@ -65,7 +69,7 @@ SDL_Surface* get_screen_surface(SDL_Window *window)
|
|||
{
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
unsigned char *pixels = (unsigned char *) malloc(24 * w * h);
|
||||
unsigned char* pixels = new unsigned char[24 * w * h];
|
||||
GLenum format;
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
format = GL_RGB;
|
||||
|
@ -76,7 +80,7 @@ SDL_Surface* get_screen_surface(SDL_Window *window)
|
|||
SDL_Surface *surface = zoomSurface(
|
||||
SDL_CreateRGBSurfaceFrom(pixels, w, h, 24, 3 * w,
|
||||
0, 0, 0, 0), 1, -1, SMOOTHING_OFF);
|
||||
free(pixels);
|
||||
delete[] pixels;
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
@ -176,6 +180,7 @@ struct Demo : Game
|
|||
Mix_Music *music = Mix_LoadMUS("resource/Field.mp3");
|
||||
Mix_PlayMusic(music, -1);
|
||||
load_gl_context();
|
||||
delegate->subscribe(&Demo::respond, this);
|
||||
Input *input = new Input(this);
|
||||
input->print_branch();
|
||||
mushroom->print_branch();
|
||||
|
@ -343,23 +348,26 @@ struct Demo : Game
|
|||
|
||||
void respond(SDL_Event& event)
|
||||
{
|
||||
if (delegate->compare(event, "context"))
|
||||
{
|
||||
if (is_gl_context)
|
||||
{
|
||||
load_sdl_context();
|
||||
}
|
||||
else
|
||||
{
|
||||
load_gl_context();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
// while (SDL_PollEvent(&event))
|
||||
// {
|
||||
// if (event.type == SDL_QUIT)
|
||||
// if (event.type == SDL_KEYDOWN)
|
||||
// {
|
||||
// flag_to_end();
|
||||
// }
|
||||
// else if (event.type == SDL_KEYDOWN)
|
||||
// {
|
||||
// if (event.key.keysym.sym == SDLK_F9)
|
||||
// {
|
||||
// capture_screen(window);
|
||||
// }
|
||||
// else if (event.key.keysym.sym == SDLK_F10)
|
||||
// if (event.key.keysym.sym == SDLK_F10)
|
||||
// {
|
||||
// if (not is_recording)
|
||||
// {
|
||||
|
@ -412,17 +420,6 @@ struct Demo : Game
|
|||
// {
|
||||
// left_active = true;
|
||||
// }
|
||||
// else if (event.key.keysym.sym == SDLK_SPACE)
|
||||
// {
|
||||
// if (is_gl_context)
|
||||
// {
|
||||
// load_sdl_context();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// load_gl_context();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else if (event.type == SDL_KEYUP)
|
||||
// {
|
||||
|
|
|
@ -37,15 +37,17 @@ $(GLEW_DIR)%.o: $(GLEW_DIR)%.c $(GLEW_DIR)%.h
|
|||
$(CC_LINUX) $(CFLAGS) $< -o $@
|
||||
|
||||
$(SFW_SRC_DIR)Sprite.o: $(addprefix $(SFW_SRC_DIR),Game.*pp Location.*pp)
|
||||
$(SFW_SRC_DIR)Game.o: $(addprefix $(SFW_SRC_DIR),Sprite.*pp Configuration.*pp Delegate.*pp)
|
||||
$(SFW_SRC_DIR)Game.o: $(addprefix $(SFW_SRC_DIR),Sprite.*pp Configuration.*pp Delegate.*pp Display.*pp Recorder.*pp)
|
||||
$(SFW_SRC_DIR)Node.o: $(addprefix $(SFW_SRC_DIR),Game.*pp Configuration.*pp)
|
||||
$(SFW_SRC_DIR)Recorder.o: $(addprefix $(SFW_SRC_DIR),extension.*pp)
|
||||
$(SFW_SRC_DIR)%.o: $(addprefix $(SFW_SRC_DIR),%.cpp %.hpp Node.*pp)
|
||||
$(CPPC_LINUX) $(CPP_FLAGS) $(SDL_FLAGS) $< -o $@
|
||||
|
||||
Demo.o: Demo.cpp Demo.hpp $(addprefix $(SFW_SRC_DIR),Sprite.*pp Node.*pp Game.*pp Location.*pp Input.*pp)
|
||||
Demo.o: Demo.cpp Demo.hpp $(addprefix $(SFW_SRC_DIR),Sprite.*pp Node.*pp Game.*pp Location.*pp Input.*pp Recorder.*pp)
|
||||
$(CPPC_LINUX) $(CPP_FLAGS) $(SDL_FLAGS) $< -o $@
|
||||
|
||||
linux: Demo.o $(addprefix $(SFW_SRC_DIR),Sprite.o Node.o Game.o Location.o Configuration.o Input.o Delegate.o) \
|
||||
linux: Demo.o $(addprefix $(SFW_SRC_DIR),Sprite.o Node.o Game.o Location.o Configuration.o Input.o Delegate.o \
|
||||
Display.o Recorder.o extension.o) \
|
||||
$(GLEW_DIR)glew.o $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o)
|
||||
$(CPPC_LINUX) $(LFLAGS) -D__LINUX__ $^ -lGL -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lstdc++fs -o demo
|
||||
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
{
|
||||
"dimensions": [640, 480]
|
||||
},
|
||||
|
||||
"path":
|
||||
{
|
||||
"screenshots": "local/screenshots"
|
||||
},
|
||||
"gamepad":
|
||||
{
|
||||
},
|
||||
|
||||
"keys":
|
||||
{
|
||||
"screenshot": ["CTRL", "s"]
|
||||
"context": " "
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,15 @@ void Configuration::set_defaults()
|
|||
{"down", "down"},
|
||||
{"left", "left"}
|
||||
};
|
||||
sys_config["path"] = {
|
||||
{"screenshots", "."}
|
||||
};
|
||||
sys_config["display"] = {
|
||||
{"dimensions", {640, 480}},
|
||||
{"screenshot-prefix", "screenshot-"},
|
||||
{"screenshot-extension", ".png"},
|
||||
{"screenshot-zfill", 5}
|
||||
};
|
||||
}
|
||||
|
||||
void Configuration::load()
|
||||
|
@ -42,7 +51,13 @@ void Configuration::merge()
|
|||
config = sys_config;
|
||||
if (not game_config.empty())
|
||||
{
|
||||
config["keys"].update(game_config["keys"]);
|
||||
for (auto& section: config.items())
|
||||
{
|
||||
if (game_config.contains(section.key()))
|
||||
{
|
||||
config[section.key()].update(game_config[section.key()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << std::setw(4) << config << std::endl;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
int Delegate::command_event_type = SDL_RegisterEvents(1);
|
||||
|
||||
Delegate::Delegate(Node *parent) : Node(parent) {}
|
||||
Delegate::Delegate(Node* parent) : Node(parent) {}
|
||||
|
||||
void Delegate::add_subscriber(subscriber s, int type)
|
||||
{
|
||||
|
@ -30,3 +30,9 @@ void Delegate::dispatch()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Delegate::compare(SDL_Event& event, std::string command)
|
||||
{
|
||||
return event.type == command_event_type and
|
||||
*static_cast<std::string*>(event.user.data1) == command;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef Delegate_h_
|
||||
#define Delegate_h_
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
|
@ -14,12 +15,19 @@ typedef std::function<void(SDL_Event&)> subscriber;
|
|||
struct Delegate : Node
|
||||
{
|
||||
|
||||
std::map<int, std::list<subscriber>> subscribers;
|
||||
std::map<int, std::vector<subscriber>> subscribers;
|
||||
static int command_event_type;
|
||||
|
||||
Delegate(Node*);
|
||||
void add_subscriber(subscriber, int);
|
||||
void dispatch();
|
||||
bool compare(SDL_Event&, std::string);
|
||||
|
||||
template<typename T>
|
||||
void subscribe(void(T::*f)(SDL_Event&), T* o, int type = command_event_type)
|
||||
{
|
||||
add_subscriber(std::bind(f, o, std::placeholders::_1), type);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#include "Display.hpp"
|
||||
#include "Game.hpp"
|
||||
|
||||
Display::Display(Node* parent) : Node(parent) {}
|
||||
|
||||
glm::ivec2 Display::get_window_size()
|
||||
{
|
||||
glm::ivec2 size;
|
||||
SDL_GetWindowSize(get_root()->window, &size.x, &size.y);
|
||||
return size;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef Display_h_
|
||||
#define Display_h_
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include "glm/vec2.hpp"
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
#include "Node.hpp"
|
||||
|
||||
struct Display : Node
|
||||
{
|
||||
|
||||
Display(Node*);
|
||||
glm::ivec2 get_window_size();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Game::Game()
|
||||
{
|
||||
delegate->add_subscriber(std::bind(&Game::handle_quit_event, this, std::placeholders::_1), SDL_QUIT);
|
||||
delegate->subscribe(&Game::handle_quit_event, this, SDL_QUIT);
|
||||
std::cout << "GLEW " << glewGetString(GLEW_VERSION) << std::endl;
|
||||
putenv("SDL_VIDEO_X11_LEGACY_FULLSCREEN=0");
|
||||
putenv("SDL_VIDEO_CENTERED=1");
|
||||
|
|
12
src/Game.hpp
12
src/Game.hpp
|
@ -18,6 +18,8 @@
|
|||
#include "Node.hpp"
|
||||
#include "Configuration.hpp"
|
||||
#include "Delegate.hpp"
|
||||
#include "Display.hpp"
|
||||
#include "Recorder.hpp"
|
||||
|
||||
struct Game : Node
|
||||
{
|
||||
|
@ -27,16 +29,18 @@ struct Game : Node
|
|||
Game(Game&&) = delete;
|
||||
Game& operator=(Game&&) = delete;
|
||||
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer = NULL;
|
||||
SDL_Window* window;
|
||||
SDL_Renderer* renderer = NULL;
|
||||
SDL_GLContext glcontext = NULL;
|
||||
int sw = 640, sh = 480, framerate = 60, frame_time_overflow = 0,
|
||||
last_frame_timestamp, frame_count_timestamp, ticks,
|
||||
last_frame_length;
|
||||
float frame_length = 1000.0 / framerate;
|
||||
bool done = false, show_framerate = false, is_gl_context = true;
|
||||
Configuration *configuration = new Configuration(this);
|
||||
Delegate *delegate = new Delegate(this);
|
||||
Configuration* configuration = new Configuration(this);
|
||||
Delegate* delegate = new Delegate(this);
|
||||
Display* display = new Display(this);
|
||||
Recorder* recorder = new Recorder(this);
|
||||
|
||||
Game();
|
||||
void print_error(std::string);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
Input::Input(Node *parent) : Node(parent)
|
||||
{
|
||||
load_key_map();
|
||||
get_delegate()->add_subscriber(std::bind(&Input::respond, this, std::placeholders::_1), SDL_KEYDOWN);
|
||||
get_delegate()->add_subscriber(std::bind(&Input::respond, this, std::placeholders::_1), Delegate::command_event_type);
|
||||
get_delegate()->subscribe(&Input::respond, this, SDL_KEYDOWN);
|
||||
get_delegate()->subscribe(&Input::respond, this);
|
||||
for (KeyCombination& combination : key_map)
|
||||
{
|
||||
print_key_combination(combination);
|
||||
|
@ -29,19 +29,10 @@ void Input::load_key_map()
|
|||
{
|
||||
for (std::string part : entry.value())
|
||||
{
|
||||
if (part == "CTRL")
|
||||
{
|
||||
ctrl = true;
|
||||
}
|
||||
else if (part == "SHIFT")
|
||||
{
|
||||
shift = true;
|
||||
}
|
||||
else if (part == "ALT")
|
||||
{
|
||||
alt = true;
|
||||
}
|
||||
else
|
||||
ctrl = part == "CTRL";
|
||||
shift = part == "SHIFT";
|
||||
alt = part == "ALT";
|
||||
if (not ctrl and not shift and not alt)
|
||||
{
|
||||
key = get_key_code(part);
|
||||
}
|
||||
|
@ -77,7 +68,6 @@ void Input::respond(SDL_Event &event)
|
|||
{
|
||||
if (event.type == SDL_KEYDOWN)
|
||||
{
|
||||
std::cout << event.key.keysym.sym << std::endl;
|
||||
SDL_Keymod mod = SDL_GetModState();
|
||||
for (KeyCombination &combination : key_map)
|
||||
{
|
||||
|
@ -93,8 +83,4 @@ void Input::respond(SDL_Event &event)
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (event.type == Delegate::command_event_type)
|
||||
{
|
||||
std::cout << event.type << " " << *((std::string*) event.user.data1) << std::endl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#ifndef Input_h_
|
||||
#define Input_h_
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
|
||||
#include "SDL.h"
|
||||
|
@ -40,7 +42,7 @@ struct Input : Node
|
|||
{"f11", SDLK_F11},
|
||||
{"f12", SDLK_F11}
|
||||
};
|
||||
std::list<KeyCombination> key_map;
|
||||
std::vector<KeyCombination> key_map;
|
||||
|
||||
Input(Node*);
|
||||
void respond(SDL_Event&);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "Node.hpp"
|
||||
#include "Game.hpp"
|
||||
#include "Display.hpp"
|
||||
|
||||
Node::Node() : Node(NULL) {}
|
||||
|
||||
|
@ -19,6 +20,11 @@ Delegate* Node::get_delegate()
|
|||
return get_root()->delegate;
|
||||
}
|
||||
|
||||
Display* Node::get_display()
|
||||
{
|
||||
return get_root()->display;
|
||||
}
|
||||
|
||||
Game* Node::get_root()
|
||||
{
|
||||
Node *current = this;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
struct Game;
|
||||
struct Delegate;
|
||||
struct Display;
|
||||
|
||||
struct Node
|
||||
{
|
||||
|
@ -21,6 +22,7 @@ struct Node
|
|||
Game* get_root();
|
||||
nlohmann::json& get_configuration();
|
||||
Delegate* get_delegate();
|
||||
Display* get_display();
|
||||
void print_branch();
|
||||
virtual std::string get_class_name() { return "Node"; };
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
#include "Recorder.hpp"
|
||||
|
||||
Recorder::Recorder(Node* parent) : Node(parent)
|
||||
{
|
||||
get_delegate()->subscribe(&Recorder::respond, this);
|
||||
}
|
||||
|
||||
void Recorder::respond(SDL_Event& event)
|
||||
{
|
||||
if (get_delegate()->compare(event, "screenshot"))
|
||||
{
|
||||
capture_screen();
|
||||
}
|
||||
}
|
||||
|
||||
void Recorder::capture_screen()
|
||||
{
|
||||
nlohmann::json config = get_configuration();
|
||||
SDL_Surface* surface = get_screen_surface();
|
||||
fs::path directory = config["path"]["screenshots"];
|
||||
fs::create_directories(directory);
|
||||
std::string prefix = config["display"]["screenshot-prefix"].
|
||||
get<std::string>();
|
||||
std::string extension = config["display"]["screenshot-extension"].
|
||||
get<std::string>();
|
||||
std::stringstream file_pattern;
|
||||
file_pattern << prefix << "(.*)" << extension;
|
||||
fs::path query = directory / file_pattern.str();
|
||||
std::vector<fs::path> files = sfw::glob(query);
|
||||
int zfill = config["display"]["screenshot-zfill"].get<int>();
|
||||
int index = 1;
|
||||
if (files.size())
|
||||
{
|
||||
const std::string last = files.back().string();
|
||||
std::smatch matches;
|
||||
std::regex_match(last, matches, std::regex(query.string()));
|
||||
index = std::stoi(matches[1]) + 1;
|
||||
}
|
||||
std::stringstream filename;
|
||||
fs::path path;
|
||||
do
|
||||
{
|
||||
filename << prefix << sfw::pad(index++, zfill) << extension;
|
||||
path = directory / filename.str();
|
||||
filename.str("");
|
||||
filename.clear();
|
||||
}
|
||||
while (fs::exists(path));
|
||||
IMG_SavePNG(surface, path.c_str());
|
||||
std::cout << "screenshot saved to " << path.string() << std::endl;
|
||||
}
|
||||
|
||||
SDL_Surface* Recorder::get_screen_surface()
|
||||
{
|
||||
glm::ivec2 size = get_display()->get_window_size();
|
||||
unsigned char* pixels = new unsigned char[24 * size.x * size.y];
|
||||
get_screen_pixels(pixels, size.x, size.y);
|
||||
SDL_Surface* surface = zoomSurface(
|
||||
SDL_CreateRGBSurfaceFrom(
|
||||
pixels, size.x, size.y, 24, 3 * size.x, 0, 0, 0, 0),
|
||||
1, -1, SMOOTHING_OFF);
|
||||
delete[] pixels;
|
||||
return surface;
|
||||
}
|
||||
|
||||
void Recorder::get_screen_pixels(unsigned char* pixels, int w, int h, int x, int y)
|
||||
{
|
||||
GLenum format;
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
format = GL_RGB;
|
||||
#else
|
||||
format = GL_BGR;
|
||||
#endif
|
||||
glReadPixels(x, y, w, h, format, GL_UNSIGNED_BYTE, pixels);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef Recorder_h_
|
||||
#define Recorder_h_
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include "glm/ext.hpp"
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#define GLEW_STATIC
|
||||
#include "glew/glew.h"
|
||||
|
||||
#include <SDL_image.h>
|
||||
#include "sdl2-gfx/SDL2_gfxPrimitives.h"
|
||||
#include "sdl2-gfx/SDL2_rotozoom.h"
|
||||
|
||||
#include "json/json.hpp"
|
||||
|
||||
#include "filesystem.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Delegate.hpp"
|
||||
#include "Display.hpp"
|
||||
#include "extension.hpp"
|
||||
|
||||
struct Recorder : Node
|
||||
{
|
||||
|
||||
Recorder(Node*);
|
||||
void respond(SDL_Event&);
|
||||
void capture_screen();
|
||||
SDL_Surface* get_screen_surface();
|
||||
void get_screen_pixels(unsigned char*, int, int, int = 0, int = 0);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
#include "extension.hpp"
|
||||
|
||||
std::vector<fs::path> sfw::glob(fs::path query)
|
||||
{
|
||||
fs::path basename = query.parent_path();
|
||||
if (basename == "")
|
||||
{
|
||||
basename = ".";
|
||||
}
|
||||
std::regex expression(query.string());
|
||||
std::vector<fs::path> files;
|
||||
std::cout << basename << " " << query << std::endl;
|
||||
for (auto& entry: fs::directory_iterator(basename))
|
||||
{
|
||||
if (std::regex_match(entry.path().string(), expression))
|
||||
{
|
||||
files.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
std::sort(files.begin(), files.end());
|
||||
return files;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef extension_h_
|
||||
#define extension_h_
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
|
||||
#include "filesystem.hpp"
|
||||
|
||||
namespace sfw
|
||||
{
|
||||
std::vector<fs::path> glob(fs::path);
|
||||
|
||||
template<typename T>
|
||||
std::string pad(T end, int width, char fill = '0')
|
||||
{
|
||||
std::stringstream padded;
|
||||
padded.fill(fill);
|
||||
padded.width(width);
|
||||
padded << end;
|
||||
return padded.str();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue