/* * This file is part of vimix - video live mixer * * **Copyright** (C) 2019-2022 Bruno Herbelin * * 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 . **/ #include #include #include #include #include // standalone image loader #include #include #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 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; }