Files
vimix/Screenshot.cpp
Bruno Herbelin 7858033628 Player Frame capture F10
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).
2022-06-22 01:40:47 +02:00

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(&copy);
// 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;
}