mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
New feature of Player: capture frame (F10 shortcut). Extending the Screenshot class for reading pixels and saving to PNG. Cleaup of screenshot (now associated to F9).
161 lines
3.9 KiB
C++
161 lines
3.9 KiB
C++
/*
|
|
* This file is part of vimix - video live mixer
|
|
*
|
|
* **Copyright** (C) 2019-2022 Bruno Herbelin <bruno.herbelin@gmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
#include <memory.h>
|
|
#include <assert.h>
|
|
#include <thread>
|
|
#include <atomic>
|
|
|
|
#include <glad/glad.h>
|
|
|
|
// standalone image loader
|
|
#include <stb_image.h>
|
|
#include <stb_image_write.h>
|
|
|
|
#include "FrameBuffer.h"
|
|
#include "Screenshot.h"
|
|
|
|
|
|
Screenshot::Screenshot()
|
|
{
|
|
Width = Height = 0;
|
|
bpp = 3;
|
|
Data = nullptr;
|
|
Pbo = 0;
|
|
Pbo_size = 0;
|
|
Pbo_full = false;
|
|
}
|
|
|
|
Screenshot::~Screenshot()
|
|
{
|
|
if (Pbo > 0)
|
|
glDeleteBuffers(1, &Pbo);
|
|
if (Data)
|
|
free(Data);
|
|
}
|
|
|
|
bool Screenshot::isFull()
|
|
{
|
|
return Pbo_full;
|
|
}
|
|
|
|
void Screenshot::capture()
|
|
{
|
|
// create BPO
|
|
if (Pbo == 0)
|
|
glGenBuffers(1, &Pbo);
|
|
|
|
// bind
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, Pbo);
|
|
|
|
// init
|
|
unsigned int size = Width * Height * bpp;
|
|
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(0, 0, Width, Height, bpp > 3 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, 0);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
// done
|
|
Pbo_full = true;
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
}
|
|
|
|
void Screenshot::captureGL(int w, int h)
|
|
{
|
|
bpp = 3; // screen capture of GL in RGB
|
|
Width = w;
|
|
Height = h;
|
|
|
|
// do capture
|
|
capture();
|
|
}
|
|
|
|
void Screenshot::captureFramebuffer(FrameBuffer *fb)
|
|
{
|
|
if (!fb)
|
|
return;
|
|
|
|
bpp = 4; // capture of FBO in RGBA
|
|
Width = fb->width() * fb->projectionArea().x;
|
|
Height = fb->height() * fb->projectionArea().y;
|
|
|
|
// blit the frame buffer into an RBBA copy of cropped size
|
|
FrameBuffer copy(Width, Height, FrameBuffer::FrameBuffer_alpha);
|
|
fb->blit(©);
|
|
|
|
// get pixels from copy
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, copy.opengl_id());
|
|
|
|
// do capture
|
|
capture();
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
void Screenshot::save(std::string filename)
|
|
{
|
|
// 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;
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
}
|
|
|
|
}
|
|
|
|
// 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) {
|
|
// save file
|
|
if (s->bpp < 4) // hack : inverse screen capture GL
|
|
stbi_flip_vertically_on_write(true);
|
|
stbi_write_png(filename.c_str(), s->Width, s->Height, s->bpp, s->Data, s->Width * s->bpp);
|
|
}
|
|
ScreenshotSavePending_ = false;
|
|
}
|