mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 15:30:00 +01:00
Clean and optimized (Pixel buffer Object and thead) Screenshot.
This commit is contained in:
@@ -148,7 +148,7 @@ message(STATUS "Including 'STB Nothings' from https://github.com/nothings/stb --
|
||||
# DIRENT
|
||||
#
|
||||
if(WIN32)
|
||||
set(DIRENT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext)
|
||||
set(DIRENT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/Dirent/include)
|
||||
message(STATUS "Including 'Dirent' from https://github.com/tronkko/dirent -- ${DIRENT_INCLUDE_DIR}.")
|
||||
endif( WIN32 )
|
||||
|
||||
|
||||
1
Mixer.h
1
Mixer.h
@@ -93,7 +93,6 @@ protected:
|
||||
GeometryView geometry_;
|
||||
LayerView layer_;
|
||||
TransitionView transition_;
|
||||
// class SessionSource *transition_source_;
|
||||
|
||||
gint64 update_time_;
|
||||
float dt_;
|
||||
|
||||
@@ -228,7 +228,7 @@ void Rendering::draw()
|
||||
// perform screenshot if requested
|
||||
if (request_screenshot_) {
|
||||
// glfwMakeContextCurrent(main_window_);
|
||||
screenshot_.CreateFromCaptureGL(0, 0, main_.width(), main_.height());
|
||||
screenshot_.captureGL(0, 0, main_.width(), main_.height());
|
||||
request_screenshot_ = false;
|
||||
}
|
||||
|
||||
@@ -353,8 +353,7 @@ Screenshot *Rendering::currentScreenshot()
|
||||
}
|
||||
|
||||
void Rendering::requestScreenshot()
|
||||
{
|
||||
screenshot_.Clear();
|
||||
{
|
||||
request_screenshot_ = true;
|
||||
}
|
||||
|
||||
|
||||
118
Screenshot.cpp
118
Screenshot.cpp
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
@@ -9,63 +11,84 @@
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
|
||||
|
||||
Screenshot::Screenshot()
|
||||
{
|
||||
Width = Height = 0;
|
||||
Data = nullptr;
|
||||
Pbo = 0;
|
||||
Pbo_size = 0;
|
||||
Pbo_full = false;
|
||||
}
|
||||
|
||||
Screenshot::~Screenshot()
|
||||
{
|
||||
Clear();
|
||||
if (Data) free(Data);
|
||||
}
|
||||
|
||||
bool Screenshot::IsFull()
|
||||
bool Screenshot::isFull()
|
||||
{
|
||||
return Data != nullptr;
|
||||
return Pbo_full;
|
||||
}
|
||||
|
||||
void Screenshot::Clear()
|
||||
void Screenshot::captureGL(int x, int y, int w, int h)
|
||||
{
|
||||
if (IsFull())
|
||||
free(Data);
|
||||
Data = nullptr;
|
||||
}
|
||||
Width = w - x;
|
||||
Height = h - y;
|
||||
unsigned int size = Width * Height * 4;
|
||||
|
||||
void Screenshot::CreateEmpty(int w, int h)
|
||||
{
|
||||
Clear();
|
||||
Width = w;
|
||||
Height = h;
|
||||
Data = (unsigned int*) malloc(Width * Height * 4 * sizeof(unsigned int));
|
||||
memset(Data, 0, Width * Height * 4);
|
||||
}
|
||||
// create BPO
|
||||
if (Pbo == 0)
|
||||
glGenBuffers(1, &Pbo);
|
||||
|
||||
void Screenshot::CreateFromCaptureGL(int x, int y, int w, int h)
|
||||
{
|
||||
Clear();
|
||||
Width = w;
|
||||
Height = h;
|
||||
Data = (unsigned int*) malloc(Width * Height * 4);
|
||||
// bind
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, Pbo);
|
||||
|
||||
// actual capture of frame buffer
|
||||
// init
|
||||
if (Pbo_size != size) {
|
||||
Pbo_size = size;
|
||||
if (Data) free(Data);
|
||||
Data = (unsigned char*) malloc(Pbo_size);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, Pbo_size, NULL, GL_STREAM_READ);
|
||||
}
|
||||
|
||||
// screenshot to PBO (fast)
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, Data);
|
||||
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
Pbo_full = true;
|
||||
|
||||
// make it usable
|
||||
RemoveAlpha();
|
||||
FlipVertical();
|
||||
// done
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
void Screenshot::SaveFile(const char* filename)
|
||||
void Screenshot::save(std::string filename)
|
||||
{
|
||||
if (Data)
|
||||
stbi_write_png(filename, Width, Height, 4, Data, Width * 4);
|
||||
// is there something to save?
|
||||
if (Pbo && Pbo_size > 0 && Pbo_full) {
|
||||
|
||||
// bind buffer
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, Pbo);
|
||||
|
||||
// get pixels (quite fast)
|
||||
unsigned char* ptr = (unsigned char*) glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
||||
if (NULL != ptr) {
|
||||
memmove(Data, ptr, Pbo_size);
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
}
|
||||
|
||||
// initiate saving in thread (slow)
|
||||
std::thread(storeToFile, this, filename).detach();
|
||||
|
||||
// ready for next
|
||||
Pbo_full = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screenshot::RemoveAlpha()
|
||||
{
|
||||
unsigned int* p = Data;
|
||||
unsigned int* p = (unsigned int*)Data;
|
||||
int n = Width * Height;
|
||||
while (n-- > 0)
|
||||
{
|
||||
@@ -74,21 +97,6 @@ void Screenshot::RemoveAlpha()
|
||||
}
|
||||
}
|
||||
|
||||
void Screenshot::BlitTo(Screenshot* dst, int src_x, int src_y, int dst_x, int dst_y, int w, int h) const
|
||||
{
|
||||
const Screenshot* src = this;
|
||||
assert(dst != src);
|
||||
assert(dst != NULL);
|
||||
assert(src_x >= 0 && src_y >= 0);
|
||||
assert(src_x + w <= src->Width);
|
||||
assert(src_y + h <= src->Height);
|
||||
assert(dst_x >= 0 && dst_y >= 0);
|
||||
assert(dst_x + w <= dst->Width);
|
||||
assert(dst_y + h <= dst->Height);
|
||||
for (int y = 0; y < h; y++)
|
||||
memcpy(dst->Data + dst_x + (dst_y + y) * dst->Width, src->Data + src_x + (src_y + y) * src->Width, w * 4);
|
||||
}
|
||||
|
||||
void Screenshot::FlipVertical()
|
||||
{
|
||||
int comp = 4;
|
||||
@@ -107,3 +115,21 @@ void Screenshot::FlipVertical()
|
||||
delete[] line_tmp;
|
||||
}
|
||||
|
||||
// Thread to perform slow operation of saving to file
|
||||
void Screenshot::storeToFile(Screenshot *s, std::string filename)
|
||||
{
|
||||
static std::atomic<bool> ScreenshotSavePending_ = false;
|
||||
// only one save at a time
|
||||
if (ScreenshotSavePending_)
|
||||
return;
|
||||
ScreenshotSavePending_ = true;
|
||||
// got data to save ?
|
||||
if (s && s->Data) {
|
||||
// make it usable
|
||||
s->RemoveAlpha();
|
||||
s->FlipVertical();
|
||||
// save file
|
||||
stbi_write_png(filename.c_str(), s->Width, s->Height, 4, s->Data, s->Width * 4);
|
||||
}
|
||||
ScreenshotSavePending_ = false;
|
||||
}
|
||||
|
||||
29
Screenshot.h
29
Screenshot.h
@@ -1,26 +1,31 @@
|
||||
#ifndef SCREENSHOT_H
|
||||
#define SCREENSHOT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class Screenshot
|
||||
{
|
||||
int Width, Height;
|
||||
unsigned int * Data;
|
||||
unsigned char * Data;
|
||||
unsigned int Pbo;
|
||||
unsigned int Pbo_size;
|
||||
bool Pbo_full;
|
||||
|
||||
void RemoveAlpha();
|
||||
void FlipVertical();
|
||||
static void storeToFile(Screenshot *s, std::string filename);
|
||||
|
||||
public:
|
||||
Screenshot();
|
||||
~Screenshot();
|
||||
|
||||
bool IsFull();
|
||||
void Clear();
|
||||
|
||||
void CreateEmpty(int w, int h);
|
||||
void CreateFromCaptureGL(int x, int y, int w, int h);
|
||||
|
||||
void RemoveAlpha();
|
||||
void FlipVertical();
|
||||
|
||||
void SaveFile(const char* filename);
|
||||
void BlitTo(Screenshot* dst, int src_x, int src_y, int dst_x, int dst_y, int w, int h) const;
|
||||
// Quick usage :
|
||||
// 1) Capture screenshot
|
||||
void captureGL(int x, int y, int w, int h);
|
||||
// 2) if it is full after capture
|
||||
bool isFull();
|
||||
// 3) then you can save to file
|
||||
void save(std::string filename);
|
||||
};
|
||||
|
||||
#endif // SCREENSHOT_H
|
||||
|
||||
@@ -299,8 +299,8 @@ void UserInterface::handleKeyboard()
|
||||
Mixer::manager().setView(View::GEOMETRY);
|
||||
else if (ImGui::IsKeyPressed( GLFW_KEY_F3 ))
|
||||
Mixer::manager().setView(View::LAYER);
|
||||
else if (ImGui::IsKeyPressed( GLFW_KEY_F4 )) // TODO REMOVE (DEBUG ONLY)
|
||||
Mixer::manager().setView(View::TRANSITION);
|
||||
// else if (ImGui::IsKeyPressed( GLFW_KEY_F4 )) // TODO REMOVE (DEBUG ONLY)
|
||||
// Mixer::manager().setView(View::TRANSITION);
|
||||
else if (ImGui::IsKeyPressed( GLFW_KEY_F11 ))
|
||||
Rendering::manager().mainWindow().toggleFullscreen();
|
||||
else if (ImGui::IsKeyPressed( GLFW_KEY_F12 ))
|
||||
@@ -311,8 +311,14 @@ void UserInterface::handleKeyboard()
|
||||
if (ImGui::IsKeyPressed( GLFW_KEY_BACKSPACE ) || ImGui::IsKeyPressed( GLFW_KEY_DELETE ))
|
||||
Mixer::manager().deleteSelection();
|
||||
// button esc to toggle fullscreen
|
||||
else if (ImGui::IsKeyPressed( GLFW_KEY_ESCAPE ))
|
||||
Rendering::manager().mainWindow().setFullscreen(nullptr);
|
||||
else if (ImGui::IsKeyPressed( GLFW_KEY_ESCAPE )) {
|
||||
// if (Rendering::manager().mainWindow().isFullscreen())
|
||||
// Rendering::manager().mainWindow().setFullscreen(nullptr);
|
||||
// else if (!Mixer::selection().empty())
|
||||
// Mixer::selection().clear();
|
||||
// else
|
||||
// Mixer::manager().setView(View::MIXING);
|
||||
}
|
||||
// button home to toggle menu
|
||||
else if (ImGui::IsKeyPressed( GLFW_KEY_HOME ))
|
||||
navigator.togglePannelMenu();
|
||||
@@ -698,10 +704,9 @@ void UserInterface::handleScreenshot()
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
if ( Rendering::manager().currentScreenshot()->IsFull() ){
|
||||
if ( Rendering::manager().currentScreenshot()->isFull() ){
|
||||
std::string filename = SystemToolkit::full_filename( SystemToolkit::home_path(), SystemToolkit::date_time_string() + "_vmixcapture.png" );
|
||||
Rendering::manager().currentScreenshot()->SaveFile( filename.c_str() );
|
||||
Rendering::manager().currentScreenshot()->Clear();
|
||||
Rendering::manager().currentScreenshot()->save( filename );
|
||||
Log::Notify("Screenshot saved %s", filename.c_str() );
|
||||
}
|
||||
screenshot_step = 4;
|
||||
@@ -1645,6 +1650,7 @@ void Navigator::RenderMainPannel()
|
||||
bool fs = Rendering::manager().mainWindow().isFullscreen();
|
||||
if ( ImGuiToolkit::IconToggle(3,15,2,15, &fs, tooltip ) ) {
|
||||
Rendering::manager().mainWindow().toggleFullscreen();
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user