Add frame capture function to Renderer

pull/3/head
Hleb Valoshka 2019-05-25 19:44:38 +03:00
parent ec7b1acec9
commit 852874b32f
14 changed files with 134 additions and 64 deletions

View File

@ -638,24 +638,30 @@ CommandCapture::CommandCapture(std::string _type,
{
}
void CommandCapture::process(ExecutionEnvironment& /*unused*/)
void CommandCapture::process(ExecutionEnvironment& env)
{
#ifndef TARGET_OS_MAC
const Renderer* r = env.getRenderer();
if (r == nullptr)
return;
// Get the dimensions of the current viewport
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
array<int, 4> viewport;
r->getScreenSize(viewport);
if (compareIgnoringCase(type, "jpeg") == 0)
{
CaptureGLBufferToJPEG(filename,
viewport[0], viewport[1],
viewport[2], viewport[3]);
viewport[2], viewport[3],
r);
}
else if (compareIgnoringCase(type, "png") == 0)
{
CaptureGLBufferToPNG(filename,
viewport[0], viewport[1],
viewport[2], viewport[3]);
viewport[2], viewport[3],
r);
}
#endif
}

View File

@ -7879,3 +7879,36 @@ void Renderer::setSolarSystemMaxDistance(float t)
if (t >= 1.0f && t <= 10.0f)
SolarSystemMaxDistance = t;
}
void Renderer::getScreenSize(int* x, int* y, int* w, int* h) const
{
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
if (x != nullptr)
*x = viewport[0];
if (y != nullptr)
*y = viewport[1];
if (w != nullptr)
*w = viewport[2];
if (h != nullptr)
*h = viewport[3];
}
void Renderer::getScreenSize(std::array<int, 4>& viewport) const
{
static_assert(sizeof(int) == sizeof(GLint), "int and GLint size mismatch");
glGetIntegerv(GL_VIEWPORT, &viewport[0]);
}
constexpr GLenum toGLFormat(Renderer::PixelFormat format)
{
return (GLenum) format;
}
bool Renderer::captureFrame(int x, int y, int w, int h, Renderer::PixelFormat format, unsigned char* buffer, bool back) const
{
glReadBuffer(back ? GL_BACK : GL_FRONT);
glReadPixels(x, y, w, h, toGLFormat(format), GL_UNSIGNED_BYTE, (void*) buffer);
return glGetError == GL_NO_ERROR;
}

View File

@ -195,6 +195,14 @@ class Renderer
StarStyleCount = 3,
};
// Pixel formats for image and video capture.
// Currently we map them 1:1 to GL
enum class PixelFormat
{
RGB = GL_RGB,
BGR_EXT = GL_BGR_EXT
};
// constants
constexpr static const uint64_t DefaultRenderFlags =
Renderer::ShowStars |
@ -237,12 +245,16 @@ class Renderer
void setOrbitMask(int);
int getScreenDpi() const;
void setScreenDpi(int);
void getScreenSize(int* x, int* y, int* w, int* h) const;
void getScreenSize(std::array<int, 4>& viewport) const;
const ColorTemperatureTable* getStarColorTable() const;
void setStarColorTable(const ColorTemperatureTable*);
bool getVideoSync() const;
void setVideoSync(bool);
void setSolarSystemMaxDistance(float);
bool captureFrame(int, int, int, int, PixelFormat format, unsigned char*, bool = false) const;
#ifdef USE_HDR
bool getBloomEnabled();
void setBloomEnabled(bool);

View File

@ -17,7 +17,8 @@
using namespace std;
AVICapture::AVICapture()
AVICapture::AVICapture(const Renderer *r) :
MovieCapture(r)
{
AVIFileInit();
}
@ -143,13 +144,13 @@ bool AVICapture::captureFrame()
return false;
// Get the dimensions of the current viewport
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
int x, y, w, h;
renderer->getScreenSize(&x, &y, &w, &h);
int x = viewport[0] + (viewport[2] - width) / 2;
int y = viewport[1] + (viewport[3] - height) / 2;
glReadPixels(x, y, width, height,
GL_BGR_EXT, GL_UNSIGNED_BYTE,
x += (w - width) / 2;
y += (h - height) / 2;
renderer->captureFrame(x, y, width, height,
Renderer::PixelFormat::BGR_EXT,
image);
int rowBytes = (width * 3 + 3) & ~0x3;

View File

@ -19,7 +19,7 @@
class AVICapture : public MovieCapture
{
public:
AVICapture();
AVICapture(const Renderer *);
virtual ~AVICapture();
bool start(const std::string& filename, int w, int h, float fps);

View File

@ -279,10 +279,11 @@ int celestia_getscreendimension(lua_State* l)
// error checking only:
this_celestia(l);
// Get the dimensions of the current viewport
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
lua_pushnumber(l, viewport[2]);
lua_pushnumber(l, viewport[3]);
int w, h;
CelestiaCore* appCore = to_celestia(l, 1);
appCore->getRenderer()->getScreenSize(nullptr, nullptr, &w, &h);
lua_pushnumber(l, w);
lua_pushnumber(l, h);
return 2;
}
@ -1920,8 +1921,8 @@ static int celestia_takescreenshot(lua_State* l)
filenamestem = fmt::sprintf("screenshot-%s%06i", fileid, luastate->screenshotCount);
// Get the dimensions of the current viewport
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
array<GLint, 4> viewport;
appCore->getRenderer()->getScreenSize(viewport);
#ifndef TARGET_OS_MAC
if (strncmp(filetype, "jpg", 3) == 0)
@ -1929,14 +1930,16 @@ static int celestia_takescreenshot(lua_State* l)
string filepath = path + filenamestem + ".jpg";
success = CaptureGLBufferToJPEG(filepath,
viewport[0], viewport[1],
viewport[2], viewport[3]);
viewport[2], viewport[3],
appCore->getRenderer());
}
else
{
string filepath = path + filenamestem + ".png";
success = CaptureGLBufferToPNG(filepath,
viewport[0], viewport[1],
viewport[2], viewport[3]);
viewport[2], viewport[3],
appCore->getRenderer());
}
#endif
lua_pushboolean(l, success);

View File

@ -1118,8 +1118,8 @@ static void openScript(const char* filename, AppData* app)
static void captureImage(const char* filename, AppData* app)
{
/* Get the dimensions of the current viewport */
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
array<int, 4> viewport;
app->renderer->getScreenSize(viewport);
bool success = false;
ContentType type = DetermineFileType(filename);
@ -1138,13 +1138,15 @@ static void captureImage(const char* filename, AppData* app)
{
success = CaptureGLBufferToJPEG(filename,
viewport[0], viewport[1],
viewport[2], viewport[3]);
viewport[2], viewport[3],
app->renderer);
}
else if (type == Content_PNG)
{
success = CaptureGLBufferToPNG(filename,
viewport[0], viewport[1],
viewport[2], viewport[3]);
viewport[2], viewport[3],
app->renderer);
}
else
{
@ -1175,10 +1177,10 @@ static void captureImage(const char* filename, AppData* app)
static void captureMovie(const char* filename, int aspect, float fps, float quality, AppData* app)
{
/* Get the dimensions of the current viewport */
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
array<int, 4> viewport;
app->renderer->getScreenSize(viewport);
MovieCapture* movieCapture = new OggTheoraCapture();
MovieCapture* movieCapture = new OggTheoraCapture(app->renderer);
switch (aspect)
{
case 0:

View File

@ -8,7 +8,6 @@
// of the License, or (at your option) any later version.
#include <celutil/debug.h>
#include <GL/glew.h>
#include <celengine/celestia.h>
#include "imagecapture.h"
@ -23,18 +22,19 @@ using namespace std;
bool CaptureGLBufferToJPEG(const string& filename,
int x, int y,
int width, int height)
int width, int height,
const Renderer *renderer)
{
int rowStride = (width * 3 + 3) & ~0x3;
int imageSize = height * rowStride;
auto* pixels = new unsigned char[imageSize];
glReadBuffer(GL_BACK);
glReadPixels(x, y, width, height,
GL_RGB, GL_UNSIGNED_BYTE,
pixels);
// TODO: Check for GL errors
if (!renderer->captureFrame(x, y, width, height,
Renderer::PixelFormat::RGB,
pixels, true))
{
return false;
}
FILE* out;
out = fopen(filename.c_str(), "wb");
@ -91,18 +91,19 @@ void PNGWriteData(png_structp png_ptr, png_bytep data, png_size_t length)
bool CaptureGLBufferToPNG(const string& filename,
int x, int y,
int width, int height)
int width, int height,
const Renderer *renderer)
{
int rowStride = (width * 3 + 3) & ~0x3;
int imageSize = height * rowStride;
auto* pixels = new unsigned char[imageSize];
glReadBuffer(GL_BACK);
glReadPixels(x, y, width, height,
GL_RGB, GL_UNSIGNED_BYTE,
pixels);
// TODO: Check for GL errors
if (!renderer->captureFrame(x, y, width, height,
Renderer::PixelFormat::RGB,
pixels, true))
{
return false;
}
FILE* out;
out = fopen(filename.c_str(), "wb");

View File

@ -11,13 +11,16 @@
#define _IMAGECAPTURE_H_
#include <string>
#include <celengine/render.h>
extern bool CaptureGLBufferToJPEG(const std::string& filename,
int x, int y,
int width, int height);
int width, int height,
const Renderer *renderer);
extern bool CaptureGLBufferToPNG(const std::string& filename,
int x, int y,
int width, int height);
int width, int height,
const Renderer *renderer);
#endif // _IMAGECAPTURE_H_

View File

@ -11,12 +11,13 @@
#define _MOVIECAPTURE_H_
#include <string>
#include <celengine/render.h>
class MovieCapture
{
public:
MovieCapture() {};
MovieCapture(const Renderer *r) : renderer(r) {};
virtual ~MovieCapture() {};
virtual bool start(const std::string& filename,
@ -33,6 +34,9 @@ class MovieCapture
virtual void setAspectRatio(int aspectNumerator, int aspectDenominator) = 0;
virtual void setQuality(float) = 0;
virtual void recordingStatus(bool started) = 0; /* to update UI recording status indicator */
protected:
const Renderer *renderer{ nullptr };
};
#endif // _MOVIECAPTURE_H_

View File

@ -84,7 +84,8 @@ using namespace std;
// {"framerate-denominator",optional_argument,nullptr,'F'},
OggTheoraCapture::OggTheoraCapture():
OggTheoraCapture::OggTheoraCapture(const Renderer *r):
MovieCapture(r),
video_x(0),
video_y(0),
frame_x(0),
@ -335,14 +336,14 @@ bool OggTheoraCapture::captureFrame()
if(ogg_stream_eos(&to)) return false;
// Get the dimensions of the current viewport
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
int x, y, w, h;
renderer->getScreenSize(&x, &y, &w, &h);
int x = viewport[0] + (viewport[2] - frame_x) / 2;
int y = viewport[1] + (viewport[3] - frame_y) / 2;
glReadPixels(x, y, frame_x, frame_y,
GL_RGB, GL_UNSIGNED_BYTE,
pixels);
x += (w - frame_x) / 2;
y += (h - frame_y) / 2;
renderer->captureFrame(x, y, frame_x, frame_y,
Renderer::PixelFormat::RGB,
pixels);
unsigned char *ybase = yuvframe[0];
unsigned char *ubase = yuvframe[0]+ video_x*video_y;

View File

@ -8,7 +8,7 @@
class OggTheoraCapture : public MovieCapture
{
public:
OggTheoraCapture();
OggTheoraCapture(const Renderer*);
virtual ~OggTheoraCapture();
bool start(const std::string& filename, int w, int h, float fps);

View File

@ -767,9 +767,9 @@ void CelestiaAppWindow::slotCaptureVideo()
float frameRate = frameRateCombo->itemData(frameRateCombo->currentIndex()).toFloat();
#ifdef _WIN32
MovieCapture* movieCapture = new AVICapture();
MovieCapture* movieCapture = new AVICapture(m_appCore->getRenderer());
#else
MovieCapture* movieCapture = new OggTheoraCapture();
MovieCapture* movieCapture = new OggTheoraCapture(m_appCore->getRenderer());
movieCapture->setAspectRatio(1, 1);
#endif
bool ok = movieCapture->start(saveAsName.toLatin1().data(),

View File

@ -433,11 +433,12 @@ static void ShowLocalTime(CelestiaCore* appCore)
}
static bool BeginMovieCapture(const std::string& filename,
static bool BeginMovieCapture(const Renderer* renderer,
const std::string& filename,
int width, int height,
float framerate)
{
MovieCapture* movieCapture = new AVICapture();
MovieCapture* movieCapture = new AVICapture(renderer);
bool success = movieCapture->start(filename, width, height, framerate);
if (success)
@ -2680,8 +2681,8 @@ static void HandleCaptureImage(HWND hWnd)
// Ofn.lpstrFileTitle contains just the filename with extension
// Get the dimensions of the current viewport
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
array<int,4> viewport;
appCore->getRenderer()->getScreenSize(viewport);
bool success = false;
@ -2727,13 +2728,15 @@ static void HandleCaptureImage(HWND hWnd)
{
success = CaptureGLBufferToJPEG(string(Ofn.lpstrFile),
viewport[0], viewport[1],
viewport[2], viewport[3]);
viewport[2], viewport[3],
appCore->getRenderer());
}
else if (nFileType == 2)
{
success = CaptureGLBufferToPNG(string(Ofn.lpstrFile),
viewport[0], viewport[1],
viewport[2], viewport[3]);
viewport[2], viewport[3],
appCore->getRenderer());
}
else
{
@ -2846,7 +2849,8 @@ static void HandleCaptureMovie(HWND hWnd)
}
else
{
success = BeginMovieCapture(string(Ofn.lpstrFile),
success = BeginMovieCapture(appCore->getRenderer(),
string(Ofn.lpstrFile),
MovieSizes[movieSize][0],
MovieSizes[movieSize][1],
MovieFramerates[movieFramerate]);