save indexed screenshot to directory

This commit is contained in:
Frank DeMarco 2019-05-07 03:33:54 -04:00
parent ebd8f34266
commit 1dbb2a2e1d
18 changed files with 284 additions and 60 deletions

View File

@ -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)
// {

View File

@ -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

View File

@ -3,13 +3,15 @@
{
"dimensions": [640, 480]
},
"path":
{
"screenshots": "local/screenshots"
},
"gamepad":
{
},
"keys":
{
"screenshot": ["CTRL", "s"]
"context": " "
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
};

11
src/Display.cpp Normal file
View File

@ -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;
}

19
src/Display.hpp Normal file
View File

@ -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

View File

@ -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");

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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&);

View File

@ -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;

View File

@ -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"; };

75
src/Recorder.cpp Normal file
View File

@ -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);
}

39
src/Recorder.hpp Normal file
View File

@ -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

22
src/extension.cpp Normal file
View File

@ -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;
}

28
src/extension.hpp Normal file
View File

@ -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