Merge branch 'master' into nodeview-redux

This commit is contained in:
itsmattkc
2021-05-29 20:57:52 +10:00
49 changed files with 674 additions and 374 deletions
+3 -3
View File
@@ -257,9 +257,9 @@ jobs:
#$DOWNLOAD_TOOL http://web.archive.org/web/20210226132532/http://download.microsoft.com/download/3/2/2/3224B87F-CFA0-4E70-BDA3-3DE650EFEBA5/vcredist_x64.exe
cp $(cygpath $GITHUB_WORKSPACE)/app/packaging/windows/nsis/* .
cp $(cygpath $GITHUB_WORKSPACE)/LICENSE .
$DOWNLOAD_TOOL https://nsis.sourceforge.io/mediawiki/images/c/c7/ShellExecAsUser.zip
7z x ShellExecAsUser.zip ShellExecAsUser.dll
makensis -V4 -DX64 "-XOutFile $PKGNAME.exe" "-X!AddPluginDir $(pwd -W)" olive.nsi
$DOWNLOAD_TOOL https://nsis.sourceforge.io/mediawiki/images/6/68/ShellExecAsUser_amd64-Unicode.7z
7z e ShellExecAsUser_amd64-Unicode.7z Plugins/x86-unicode/ShellExecAsUser.dll
makensis -V4 -DX64 "-XOutFile $PKGNAME.exe" "-X!AddPluginDir /x86-unicode $(pwd -W)" olive.nsi
# Create Portable ZIP
echo -n > olive-editor/portable
+3 -1
View File
@@ -162,7 +162,7 @@ int main(int argc, char *argv[])
//
// https://bugreports.qt.io/browse/QTBUG-46140
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
format.setVersion(3, 2);
format.setVersion(2, 0);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setOption(QSurfaceFormat::DeprecatedFunctions);
@@ -172,6 +172,8 @@ int main(int argc, char *argv[])
// Enable application automatically using higher resolution images from icons
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
// Create application instance
std::unique_ptr<QCoreApplication> a;
+3 -1
View File
@@ -1,4 +1,4 @@
!include "MUI.nsh"
!include "MUI2.nsh"
!define MUI_ICON "install icon.ico"
!define MUI_UNICON "uninstall icon.ico"
@@ -12,6 +12,8 @@ SetCompressor lzma
Name ${APP_NAME}
ManifestDPIAware true
Unicode true
!ifdef X64
InstallDir "$PROGRAMFILES64\${APP_NAME}"
+1 -1
View File
@@ -84,7 +84,7 @@ QString ScopePanel::TypeToName(ScopePanel::Type t)
return QString();
}
void ScopePanel::SetReferenceBuffer(Frame *frame)
void ScopePanel::SetReferenceBuffer(TexturePtr frame)
{
histogram_->SetBuffer(frame);
waveform_view_->SetBuffer(frame);
+1 -1
View File
@@ -50,7 +50,7 @@ public:
static QString TypeToName(Type t);
public slots:
void SetReferenceBuffer(Frame* frame);
void SetReferenceBuffer(TexturePtr frame);
void SetColorManager(ColorManager* manager);
+1 -1
View File
@@ -108,7 +108,7 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type)
p->SetType(type);
// Connect viewer widget texture drawing to scope panel
connect(vw, &ViewerWidget::LoadedBuffer, p, &ScopePanel::SetReferenceBuffer);
connect(vw, &ViewerWidget::TextureChanged, p, &ScopePanel::SetReferenceBuffer);
connect(vw, &ViewerWidget::ColorManagerChanged, p, &ScopePanel::SetColorManager);
p->SetColorManager(vw->color_manager());
+137 -65
View File
@@ -76,6 +76,11 @@ private:
#define PRINT_GL_ERRORS ErrorPrinter __e(__FUNCTION__, functions_)
#define GL_PREAMBLE \
QMutexLocker __l(&global_opengl_mutex);
QMutex global_opengl_mutex;
OpenGLRenderer::OpenGLRenderer(QObject* parent) :
Renderer(parent),
cache_timer_(this),
@@ -104,6 +109,8 @@ void OpenGLRenderer::Init(QOpenGLContext *existing_ctx)
bool OpenGLRenderer::Init()
{
QMutexLocker locker(&global_opengl_mutex);
if (context_) {
qCritical() << "Can't initialize already initialized OpenGLRenderer";
return false;
@@ -112,6 +119,7 @@ bool OpenGLRenderer::Init()
surface_.create();
context_ = new QOpenGLContext(this);
context_->setShareContext(QOpenGLContext::globalShareContext());
if (!context_->create()) {
qCritical() << "Failed to create OpenGL context";
return false;
@@ -132,6 +140,8 @@ void OpenGLRenderer::PostDestroy()
void OpenGLRenderer::PostInit()
{
GL_PREAMBLE;
// Make context current on that surface
if (context_->parent() == this && !context_->makeCurrent(&surface_)) {
qCritical() << "Failed to makeCurrent() on offscreen surface in thread" << thread();
@@ -152,6 +162,8 @@ void OpenGLRenderer::PostInit()
void OpenGLRenderer::DestroyInternal()
{
if (context_) {
GL_PREAMBLE;
// Delete framebuffer
functions_->glDeleteFramebuffers(1, &framebuffer_);
framebuffer_ = 0;
@@ -171,55 +183,32 @@ void OpenGLRenderer::DestroyInternal()
cache_timer_.stop();
}
void OpenGLRenderer::ClearDestination(double r, double g, double b, double a)
void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, double b, double a)
{
functions_->glClearColor(r, g, b, a);
functions_->glClear(GL_COLOR_BUFFER_BIT);
GL_PREAMBLE;
if (texture) {
AttachTextureAsDestination(texture);
}
ClearDestinationInternal(r, g, b, a);
if (texture) {
DetachTextureAsDestination();
}
}
QVariant OpenGLRenderer::CreateNativeTexture2D(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize)
{
GLuint texture = GetCachedTexture(width, height, 1, format, channel_count);
GL_PREAMBLE;
// If no texture in cache, generate new texture
bool new_tex = (texture == 0);
if (new_tex) {
functions_->glGenTextures(1, &texture);
texture_params_.insert(texture, {width, height, 1, format, channel_count});
}
if (new_tex || data) {
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
GLint current_tex;
functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_tex);
functions_->glBindTexture(GL_TEXTURE_2D, texture);
{
PRINT_GL_ERRORS;
if (new_tex) {
functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(format, channel_count),
width, height, 0, GetPixelFormat(channel_count),
GetPixelType(format), data);
} else {
functions_->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
width, height,
GetPixelFormat(channel_count), GetPixelType(format),
data);
}
}
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
functions_->glBindTexture(GL_TEXTURE_2D, current_tex);
}
return texture;
return CreateNativeTexture2DInternal(width, height, format, channel_count, data, linesize);
}
QVariant OpenGLRenderer::CreateNativeTexture3D(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize)
{
GL_PREAMBLE;
GLuint texture = GetCachedTexture(width, height, depth, format, channel_count);
// If no texture in cache, generate new texture
@@ -287,6 +276,8 @@ void OpenGLRenderer::DestroyNativeTexture(QVariant texture)
QVariant OpenGLRenderer::CreateNativeShader(ShaderCode code)
{
GL_PREAMBLE;
PRINT_GL_ERRORS;
QOpenGLShaderProgram* program = new QOpenGLShaderProgram(context_);
@@ -294,17 +285,8 @@ QVariant OpenGLRenderer::CreateNativeShader(ShaderCode code)
QString vert_code = code.vert_code();
QString frag_code = code.frag_code();
QString shader_preamble;
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
shader_preamble = QStringLiteral("#version 300 es\n"
"\n"
"precision highp int;\n"
"precision highp float;\n"
"\n");
} else {
shader_preamble = QStringLiteral("#version 150\n"
"\n");
}
QString shader_preamble = QStringLiteral("#version 110\n"
"\n");
vert_code.prepend(shader_preamble);
frag_code.prepend(shader_preamble);
@@ -333,12 +315,14 @@ error:
void OpenGLRenderer::DestroyNativeShader(QVariant shader)
{
GL_PREAMBLE;
delete Node::ValueToPtr<QOpenGLShaderProgram>(shader);
}
void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int linesize)
{
PRINT_GL_ERRORS;
GL_PREAMBLE;
GLuint t = texture->id().value<GLuint>();
const VideoParams& p = texture->params();
@@ -354,16 +338,20 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
if (texture->type() == Texture::k2D) {
functions_->glTexSubImage2D(tex_type, 0, 0, 0,
p.effective_width(), p.effective_height(),
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
data);
} else {
context_->extraFunctions()->glTexSubImage3D(tex_type, 0, 0, 0, 0,
p.effective_width(), p.effective_height(), p.effective_depth(),
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
data);
{
PRINT_GL_ERRORS;
if (texture->type() == Texture::k2D) {
functions_->glTexSubImage2D(tex_type, 0, 0, 0,
p.effective_width(), p.effective_height(),
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
data);
} else {
context_->extraFunctions()->glTexSubImage3D(tex_type, 0, 0, 0, 0,
p.effective_width(), p.effective_height(), p.effective_depth(),
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
data);
}
}
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
@@ -373,6 +361,8 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin
void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int linesize)
{
GL_PREAMBLE;
const VideoParams& p = texture->params();
GLint current_tex;
@@ -388,7 +378,7 @@ void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int lines
0,
p.effective_width(),
p.effective_height(),
(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) ? GL_RGBA : GetPixelFormat(p.channel_count()),
GetPixelFormat(p.channel_count()),
GetPixelType(p.format()),
data);
}
@@ -400,6 +390,33 @@ void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int lines
functions_->glBindTexture(GL_TEXTURE_2D, current_tex);
}
void OpenGLRenderer::Flush()
{
GL_PREAMBLE;
functions_->glFinish();
}
Color OpenGLRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
{
AttachTextureAsDestination(texture);
QByteArray data(VideoParams::GetBytesPerPixel(texture->format(), texture->channel_count()), Qt::Uninitialized);
functions_->glReadPixels(pt.x(), pt.y(), 1, 1, GetPixelFormat(texture->channel_count()), GetPixelType(texture->format()), data.data());
Color c = Color::fromData(data.data(), texture->format(), texture->channel_count());
if (texture->channel_count() == VideoParams::kRGBChannelCount) {
// No alpha channel, set to 1.0
c.set_alpha(1.0);
}
DetachTextureAsDestination();
return c;
}
struct TextureToBind {
TexturePtr texture;
Texture::Interpolation interpolation;
@@ -407,6 +424,8 @@ struct TextureToBind {
void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, VideoParams destination_params, bool clear_destination)
{
GL_PREAMBLE;
// If this node is iterative, we'll pick up which input here
QString iterative_name;
GLuint iterative_input = 0;
@@ -581,11 +600,11 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
TexturePtr output_tex, input_tex;
if (real_iteration_count > 1) {
// Create one texture to bounce off
output_tex = CreateTexture(destination_params);
output_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params);
if (real_iteration_count > 2) {
// Create a second texture bounce off
input_tex = CreateTexture(destination_params);
input_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params);
}
}
@@ -606,7 +625,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
// Clear the destination if the caller requested it
if (clear_destination) {
ClearDestination();
ClearDestinationInternal();
}
} else {
// Always draw to output_tex, which gets swapped with input_tex every iteration
@@ -771,6 +790,58 @@ void OpenGLRenderer::PrepareInputTexture(GLenum target, Texture::Interpolation i
functions_->glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
void OpenGLRenderer::ClearDestinationInternal(double r, double g, double b, double a)
{
functions_->glClearColor(r, g, b, a);
functions_->glClear(GL_COLOR_BUFFER_BIT);
}
QVariant OpenGLRenderer::CreateNativeTexture2DInternal(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize)
{
GLuint texture = GetCachedTexture(width, height, 1, format, channel_count);
// If no texture in cache, generate new texture
bool new_tex = (texture == 0);
if (new_tex) {
functions_->glGenTextures(1, &texture);
texture_params_.insert(texture, {width, height, 1, format, channel_count});
}
if (new_tex || data) {
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
GLint current_tex;
functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_tex);
functions_->glBindTexture(GL_TEXTURE_2D, texture);
{
PRINT_GL_ERRORS;
if (new_tex) {
functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(format, channel_count),
width, height, 0, GetPixelFormat(channel_count),
GetPixelType(format), data);
} else {
functions_->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
width, height,
GetPixelFormat(channel_count), GetPixelType(format),
data);
}
}
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
functions_->glBindTexture(GL_TEXTURE_2D, current_tex);
}
return texture;
}
QVariant OpenGLRenderer::CreateNativeTexture2DInternal(const VideoParams &params, const void *data, int linesize)
{
return CreateNativeTexture2DInternal(params.effective_width(), params.effective_height(), params.format(), params.channel_count(), data, linesize);
}
GLuint OpenGLRenderer::GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count)
{
TextureCacheKey input_key = {width, height, depth, format, channel_count};
@@ -795,6 +866,7 @@ void OpenGLRenderer::GarbageCollectTextureCache()
qint64 max_age = QDateTime::currentMSecsSinceEpoch() - kTextureCacheMaxSize;
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); ) {
if (it->age < max_age) {
GL_PREAMBLE;
GLuint t = it->texture;
texture_params_.remove(t);
functions_->glDeleteTextures(1, &t);
+10 -1
View File
@@ -52,7 +52,7 @@ public slots:
virtual void DestroyInternal() override;
virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override;
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override;
virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
@@ -67,6 +67,10 @@ public slots:
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override;
virtual void Flush() override;
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override;
protected slots:
virtual void Blit(QVariant shader,
olive::ShaderJob job,
@@ -87,6 +91,11 @@ private:
void PrepareInputTexture(GLenum target, Texture::Interpolation interp);
void ClearDestinationInternal(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0);
QVariant CreateNativeTexture2DInternal(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0);
QVariant CreateNativeTexture2DInternal(const VideoParams &params, const void* data = nullptr, int linesize = 0);
GLuint GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count);
QTimer cache_timer_;
+6 -4
View File
@@ -530,7 +530,8 @@ void PreviewAutoCacher::TryRender()
} else {
watcher = RenderFrame(hash,
single_frame_render_->property("time").value<rational>(),
single_frame_render_->property("prioritize").toBool());
single_frame_render_->property("prioritize").toBool(),
paused_);
video_immediate_passthroughs_[watcher].append(single_frame_render_);
}
@@ -539,7 +540,7 @@ void PreviewAutoCacher::TryRender()
}
}
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, const rational& time, bool prioritize)
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, const rational& time, bool prioritize, bool texture_only)
{
RenderTicketWatcher* watcher = new RenderTicketWatcher();
watcher->setProperty("hash", hash);
@@ -550,7 +551,8 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, cons
time,
RenderMode::kOffline,
viewer_node_->video_frame_cache(),
prioritize));
prioritize,
texture_only));
return watcher;
}
@@ -585,7 +587,7 @@ void PreviewAutoCacher::RequeueFrames()
// We want this hash, if we're not already rendering, start render now
if (!render_task && !video_download_tasks_.key(hash)) {
// Don't render any hash more than once
RenderFrame(hash, t, false);
RenderFrame(hash, t, false, false);
}
} else if (render_task) {
// Cancel this frame unless it's already started
+1 -1
View File
@@ -84,7 +84,7 @@ private:
void TryRender();
RenderTicketWatcher *RenderFrame(const QByteArray& hash, const rational &time, bool prioritize);
RenderTicketWatcher *RenderFrame(const QByteArray& hash, const rational &time, bool prioritize, bool texture_only);
/**
* @brief Process all changes to internal NodeGraph copy
+44 -18
View File
@@ -20,6 +20,8 @@
#include "renderer.h"
#include <QVector2D>
#include "common/ocioutils.h"
namespace olive {
@@ -42,11 +44,7 @@ TexturePtr Renderer::CreateTexture(const VideoParams &params, Texture::Type type
params.channel_count(), data, linesize);
}
if (v.isNull()) {
return nullptr;
}
return std::make_shared<Texture>(this, v, params, type);
return CreateTextureFromNativeHandle(v, params, type);
}
TexturePtr Renderer::CreateTexture(const VideoParams &params, const void *data, int linesize)
@@ -64,13 +62,47 @@ void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr so
BlitColorManagedInternal(color_processor, source, source_is_premultiplied, nullptr, params, clear_destination, matrix, crop_matrix);
}
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams &params)
{
color_cache_mutex_.lock();
if (interlace_texture_.isNull()) {
interlace_texture_ = CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/interlace.frag"))));
}
color_cache_mutex_.unlock();
ShaderJob job;
job.InsertValue(QStringLiteral("top_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(top)));
job.InsertValue(QStringLiteral("bottom_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(bottom)));
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(params.effective_width(), params.effective_height())));
TexturePtr output = CreateTexture(params);
BlitToTexture(interlace_texture_, job, output.get());
return output;
}
void Renderer::Destroy()
{
color_cache_.clear();
if (!interlace_texture_.isNull()) {
DestroyNativeShader(interlace_texture_);
interlace_texture_.clear();
}
DestroyInternal();
}
TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams &params, Texture::Type type)
{
if (v.isNull()) {
return nullptr;
}
return std::make_shared<Texture>(this, v, params, type);
}
bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::ColorContext *ctx)
{
QMutexLocker locker(&color_cache_mutex_);
@@ -103,15 +135,9 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo
"#define ALPHA_UNASSOC 1\n"
"#define ALPHA_ASSOC 2\n"
"\n"
"// Macros so OCIO's shaders work on this GLSL version\n"
"#define texture2D texture\n"
"#define texture3D texture\n"
"\n"
"// Main texture coordinate\n"
"in vec2 ove_texcoord;\n"
"\n"
"// Texture output\n"
"out vec4 fragColor;\n"));
"varying vec2 ove_texcoord;\n"
"\n"));
shader_frag.append(shader_desc->getShaderText());
shader_frag.append(QStringLiteral("\n"
"// Alpha association functions\n"
@@ -128,13 +154,13 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo
"}\n"
"\n"
"void main() {\n"
" vec2 cropped_coord = (vec4(ove_texcoord-vec2(0.5, 0.5), 0.0, 1.0)*inverse(ove_cropmatrix)).xy + vec2(0.5, 0.5);\n"
" vec2 cropped_coord = (vec4(ove_texcoord-vec2(0.5, 0.5), 0.0, 1.0)*ove_cropmatrix).xy + vec2(0.5, 0.5);\n"
" if (cropped_coord.x < 0.0 || cropped_coord.x >= 1.0 || cropped_coord.y < 0.0 || cropped_coord.y >= 1.0) {\n"
" fragColor = vec4(0.0);\n"
" gl_FragColor = vec4(0.0);\n"
" return;\n"
" }\n"
" \n"
" vec4 col = texture(ove_maintex, cropped_coord);\n"
" vec4 col = texture2D(ove_maintex, cropped_coord);\n"
"\n"
" // If alpha is associated, de-associate now\n"
" if (ove_maintex_alpha == ALPHA_ASSOC) {\n"
@@ -151,7 +177,7 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo
" col = assoc(col);\n"
" }\n"
"\n"
" fragColor = col;\n"
" gl_FragColor = col;\n"
"}\n").arg(ocio_func_name));
// Try to compile shader
@@ -244,7 +270,7 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(source)));
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix));
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix.inverted()));
AlphaAssociated associated;
if (source->channel_count() == VideoParams::kRGBAChannelCount) {
+12 -1
View File
@@ -66,6 +66,8 @@ public:
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, Texture* destination, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, VideoParams params, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams &params);
void Destroy();
virtual void PostDestroy() = 0;
@@ -75,7 +77,7 @@ public slots:
virtual void DestroyInternal() = 0;
virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 0;
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 0;
virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0;
virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0;
@@ -90,6 +92,10 @@ public slots:
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) = 0;
virtual void Flush() = 0;
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) = 0;
protected slots:
virtual void Blit(QVariant shader,
olive::ShaderJob job,
@@ -97,6 +103,9 @@ protected slots:
olive::VideoParams destination_params,
bool clear_destination) = 0;
protected:
TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams &params, Texture::Type type = Texture::k2D);
private:
struct ColorContext {
struct LUT {
@@ -128,6 +137,8 @@ private:
QMutex color_cache_mutex_;
QVariant interlace_texture_;
};
}
+19 -1
View File
@@ -69,9 +69,10 @@ void RendererThreadWrapper::DestroyInternal()
}
}
void RendererThreadWrapper::ClearDestination(double r, double g, double b, double a)
void RendererThreadWrapper::ClearDestination(Texture *texture, double r, double g, double b, double a)
{
QMetaObject::invokeMethod(inner_, "ClearDestination", Qt::BlockingQueuedConnection,
OLIVE_NS_ARG(Texture*, texture),
Q_ARG(double, r),
Q_ARG(double, g),
Q_ARG(double, b),
@@ -150,6 +151,23 @@ void RendererThreadWrapper::DownloadFromTexture(Texture *texture, void *data, in
Q_ARG(int, linesize));
}
void RendererThreadWrapper::Flush()
{
QMetaObject::invokeMethod(inner_, "Flush", Qt::BlockingQueuedConnection);
}
Color RendererThreadWrapper::GetPixelFromTexture(Texture *texture, const QPointF &pt)
{
Color c;
QMetaObject::invokeMethod(inner_, "GetPixelFromTexture", Qt::BlockingQueuedConnection,
OLIVE_NS_RETURN_ARG(Color, c),
OLIVE_NS_ARG(Texture*, texture),
Q_ARG(QPointF, pt));
return c;
}
void RendererThreadWrapper::Blit(QVariant shader, ShaderJob job, Texture *destination, VideoParams destination_params, bool clear_destination)
{
QMetaObject::invokeMethod(inner_, "Blit", Qt::BlockingQueuedConnection,
+5 -1
View File
@@ -48,7 +48,7 @@ public slots:
virtual void DestroyInternal() override;
virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override;
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override;
virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
@@ -63,6 +63,10 @@ public slots:
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override;
virtual void Flush() override;
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override;
protected slots:
virtual void Blit(QVariant shader,
olive::ShaderJob job,
+5 -3
View File
@@ -126,7 +126,7 @@ QByteArray RenderManager::Hash(const Node *n, const QString& output, const Video
RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* color_manager,
const rational& time, RenderMode::Mode mode,
FrameHashCache* cache, bool prioritize)
FrameHashCache* cache, bool prioritize, bool texture_only)
{
return RenderFrame(viewer,
color_manager,
@@ -139,7 +139,8 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c
VideoParams::kFormatInvalid,
nullptr,
cache,
prioritize);
prioritize,
texture_only);
}
RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* color_manager,
@@ -148,7 +149,7 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c
const QSize& force_size,
const QMatrix4x4& force_matrix, VideoParams::Format force_format,
ColorProcessorPtr force_color_output,
FrameHashCache* cache, bool prioritize)
FrameHashCache* cache, bool prioritize, bool texture_only)
{
// Create ticket
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
@@ -164,6 +165,7 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c
ticket->setProperty("coloroutput", QVariant::fromValue(force_color_output));
ticket->setProperty("vparam", QVariant::fromValue(video_params));
ticket->setProperty("aparam", QVariant::fromValue(audio_params));
ticket->setProperty("textureonly", texture_only);
if (cache) {
ticket->setProperty("cache", cache->GetCacheDirectory());
+2 -2
View File
@@ -86,14 +86,14 @@ public:
*/
RenderTicketPtr RenderFrame(ViewerOutput *viewer, ColorManager* color_manager,
const rational& time, RenderMode::Mode mode,
FrameHashCache* cache = nullptr, bool prioritize = false);
FrameHashCache* cache = nullptr, bool prioritize = false, bool texture_only = false);
RenderTicketPtr RenderFrame(ViewerOutput* viewer, ColorManager* color_manager,
const rational& time, RenderMode::Mode mode,
const VideoParams& video_params, const AudioParams& audio_params,
const QSize& force_size,
const QMatrix4x4& force_matrix, VideoParams::Format force_format,
ColorProcessorPtr force_color_output,
FrameHashCache* cache = nullptr, bool prioritize = false);
FrameHashCache* cache = nullptr, bool prioritize = false, bool texture_only = false);
/**
* @brief Asynchronously generate a chunk of audio
+26 -13
View File
@@ -40,7 +40,7 @@ RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, S
{
}
FramePtr RenderProcessor::GenerateFrame(const rational& time, const rational& frame_length)
TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational &frame_length)
{
ViewerOutput* viewer = Node::ValueToPtr<ViewerOutput>(ticket_->property("viewer"));
@@ -51,8 +51,11 @@ FramePtr RenderProcessor::GenerateFrame(const rational& time, const rational& fr
TimeRange(time, time + frame_length));
}
TexturePtr texture = table.Get(NodeValue::kTexture).value<TexturePtr>();
return table.Get(NodeValue::kTexture).value<TexturePtr>();
}
FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time)
{
// Set up output frame parameters
VideoParams frame_params = GetCacheVideoParams();
@@ -128,25 +131,35 @@ void RenderProcessor::Run()
frame_length /= 2;
}
FramePtr frame = GenerateFrame(time, frame_length);
TexturePtr texture = GenerateTexture(time, frame_length);
if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) {
// Get next between frame and interlace it
FramePtr next_frame = GenerateFrame(time + frame_length, frame_length);
TexturePtr top = texture;
TexturePtr bottom = GenerateTexture(time + frame_length, frame_length);
FramePtr top, bottom;
if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedTopFirst) {
top = frame;
bottom = next_frame;
} else {
top = next_frame;
bottom = frame;
if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedBottomFirst) {
std::swap(top, bottom);
}
frame = Frame::Interlace(top, bottom);
texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams());
}
ticket_->Finish(QVariant::fromValue(frame));
if (ticket_->property("textureonly").toBool()) {
// Return GPU texture
if (!texture) {
texture = render_ctx_->CreateTexture(GetCacheVideoParams());
}
render_ctx_->Flush();
ticket_->Finish(QVariant::fromValue(texture));
} else {
// Convert to CPU frame
FramePtr frame = GenerateFrame(texture, time);
ticket_->Finish(QVariant::fromValue(frame));
}
break;
}
case RenderManager::kTypeAudio:
+3 -1
View File
@@ -62,7 +62,9 @@ protected:
private:
RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, StillImageCache* still_image_cache, DecoderCache* decoder_cache, ShaderCache* shader_cache, QVariant default_shader);
FramePtr GenerateFrame(const rational &time, const rational &frame_length);
TexturePtr GenerateTexture(const rational& time, const rational& frame_length);
FramePtr GenerateFrame(TexturePtr texture, const rational &time);
void Run();
+5
View File
@@ -120,6 +120,11 @@ public:
return type_;
}
Renderer* renderer() const
{
return renderer_;
}
private:
Renderer* renderer_;
+8 -10
View File
@@ -3,31 +3,29 @@ uniform sampler2D blend_in;
uniform bool base_in_enabled;
uniform bool blend_in_enabled;
in vec2 ove_texcoord;
out vec4 fragColor;
varying vec2 ove_texcoord;
void main(void) {
vec4 base_col = texture(base_in, ove_texcoord);
vec4 blend_col = texture(blend_in, ove_texcoord);
vec4 base_col = texture2D(base_in, ove_texcoord);
vec4 blend_col = texture2D(blend_in, ove_texcoord);
if (!base_in_enabled && !blend_in_enabled) {
fragColor = vec4(0.0);
gl_FragColor = vec4(0.0);
return;
}
if (!base_in_enabled) {
fragColor = blend_col;
gl_FragColor = blend_col;
return;
}
if (!blend_in_enabled) {
fragColor = base_col;
return;
gl_FragColor = base_col;
return;
}
base_col *= 1.0 - blend_col.a;
base_col += blend_col;
fragColor = base_col;
gl_FragColor = base_col;
}
+4 -6
View File
@@ -8,9 +8,7 @@ uniform vec2 resolution_in;
uniform int ove_iteration;
in vec2 ove_texcoord;
out vec4 fragColor;
varying vec2 ove_texcoord;
// Gaussian function uses PI
#define M_PI 3.1415926535897932384626433832795
@@ -65,7 +63,7 @@ void main(void) {
int mode = determine_mode();
if (mode == MODE_NONE) {
fragColor = texture(tex_in, ove_texcoord);
gl_FragColor = texture2D(tex_in, ove_texcoord);
return;
}
@@ -117,9 +115,9 @@ void main(void) {
&& pixel_coord.x < 1.0
&& pixel_coord.y >= 0.0
&& pixel_coord.y < 1.0)) {
composite += texture(tex_in, pixel_coord) * weight;
composite += texture2D(tex_in, pixel_coord) * weight;
}
}
fragColor = composite;
gl_FragColor = composite;
}
+4 -7
View File
@@ -8,10 +8,7 @@ uniform float feather_in;
uniform vec2 resolution_in;
// Input texture coordinate
in vec2 ove_texcoord;
// Output color
out vec4 fragColor;
varying vec2 ove_texcoord;
void main() {
float multiplier = 1.0;
@@ -47,9 +44,9 @@ void main() {
}
if (multiplier > 0.0) {
vec4 color = texture(tex_in, ove_texcoord) * multiplier;
fragColor = color;
vec4 color = texture2D(tex_in, ove_texcoord) * multiplier;
gl_FragColor = color;
} else {
fragColor = vec4(0.0);
gl_FragColor = vec4(0.0);
}
}
+4 -6
View File
@@ -10,9 +10,7 @@ uniform int curve_in;
uniform float ove_tprog_all;
in vec2 ove_texcoord;
out vec4 fragColor;
varying vec2 ove_texcoord;
float TransformCurve(float linear) {
if (curve_in == EXPONENTIAL_CURVE) {
@@ -28,12 +26,12 @@ void main(void) {
vec4 composite = vec4(0.0);
if (out_block_in_enabled) {
composite += texture(out_block_in, ove_texcoord) * TransformCurve(1.0 - ove_tprog_all);
composite += texture2D(out_block_in, ove_texcoord) * TransformCurve(1.0 - ove_tprog_all);
}
if (in_block_in_enabled) {
composite += texture(in_block_in, ove_texcoord) * TransformCurve(ove_tprog_all);
composite += texture2D(in_block_in, ove_texcoord) * TransformCurve(ove_tprog_all);
}
fragColor = composite;
gl_FragColor = composite;
}
+3 -6
View File
@@ -2,12 +2,9 @@
uniform sampler2D ove_maintex;
// Input texture coordinate
in vec2 ove_texcoord;
// Output color
out vec4 fragColor;
varying vec2 ove_texcoord;
void main() {
vec4 color = texture(ove_maintex, ove_texcoord);
fragColor = color;
vec4 color = texture2D(ove_maintex, ove_texcoord);
gl_FragColor = color;
}
+4 -4
View File
@@ -1,11 +1,11 @@
uniform mat4 ove_mvpmat;
in vec4 a_position;
in vec2 a_texcoord;
attribute vec4 a_position;
attribute vec2 a_texcoord;
out vec2 ove_texcoord;
varying vec2 ove_texcoord;
void main() {
gl_Position = ove_mvpmat * a_position;
ove_texcoord = a_texcoord;
}
}
+7 -4
View File
@@ -2,9 +2,12 @@ uniform sampler2D ove_maintex;
uniform vec2 resolution_in;
in vec2 ove_texcoord;
varying vec2 ove_texcoord;
out vec4 fragColor;
float round(float x)
{
return floor(x + 0.5);
}
void main() {
vec2 using_texcoord = ove_texcoord;
@@ -15,6 +18,6 @@ void main() {
float half_vert = round(resolution_in.y / 2.0);
using_texcoord.y = (round(using_texcoord.y * half_vert) + 0.25) / half_vert;
vec4 color = texture(ove_maintex, using_texcoord);
fragColor = color;
vec4 color = texture2D(ove_maintex, using_texcoord);
gl_FragColor = color;
}
+7 -9
View File
@@ -8,21 +8,19 @@ uniform float ove_tprog_all;
uniform float ove_tprog_out;
uniform float ove_tprog_in;
in vec2 ove_texcoord;
out vec4 fragColor;
varying vec2 ove_texcoord;
void main(void) {
if (out_block_in_enabled && in_block_in_enabled) {
vec4 out_block_col = mix(texture(out_block_in, ove_texcoord), color_in, ove_tprog_out);
vec4 in_block_col = mix(texture(in_block_in, ove_texcoord), color_in, 1.0 - ove_tprog_in);
vec4 out_block_col = mix(texture2D(out_block_in, ove_texcoord), color_in, ove_tprog_out);
vec4 in_block_col = mix(texture2D(in_block_in, ove_texcoord), color_in, 1.0 - ove_tprog_in);
fragColor = out_block_col + in_block_col;
gl_FragColor = out_block_col + in_block_col;
} else if (out_block_in_enabled) {
fragColor = mix(texture(out_block_in, ove_texcoord), color_in, ove_tprog_all);
gl_FragColor = mix(texture2D(out_block_in, ove_texcoord), color_in, ove_tprog_all);
} else if (in_block_in_enabled) {
fragColor = mix(texture(in_block_in, ove_texcoord), color_in, 1.0 - ove_tprog_all);
gl_FragColor = mix(texture2D(in_block_in, ove_texcoord), color_in, 1.0 - ove_tprog_all);
} else {
fragColor = vec4(0.0);
gl_FragColor = vec4(0.0);
}
}
+15
View File
@@ -0,0 +1,15 @@
uniform sampler2D top_tex_in;
uniform sampler2D bottom_tex_in;
uniform vec2 resolution_in;
varying vec2 ove_texcoord;
void main() {
float y_pixel = floor(ove_texcoord.y * resolution_in.y);
if (mod(y_pixel, 2.0) == 0.0) {
gl_FragColor = texture2D(top_tex_in, ove_texcoord);
} else {
gl_FragColor = texture2D(bottom_tex_in, ove_texcoord);
}
}
+3 -6
View File
@@ -5,10 +5,7 @@ uniform float horiz_in;
uniform float vert_in;
// Input texture coordinate
in vec2 ove_texcoord;
// Output color
out vec4 fragColor;
varying vec2 ove_texcoord;
void main() {
float x;
@@ -26,6 +23,6 @@ void main() {
y = ove_texcoord.y;
}
vec4 color = texture(tex_in, vec2(x, y));
fragColor = color;
vec4 color = texture2D(tex_in, vec2(x, y));
gl_FragColor = color;
}
+3 -5
View File
@@ -4,9 +4,7 @@ uniform vec4 color_in;
uniform vec2 resolution_in;
in vec2 ove_texcoord;
out vec4 fragColor;
varying vec2 ove_texcoord;
/*
int pnpoly(int npol, float *xp, float *yp, float x, float y) {
@@ -35,8 +33,8 @@ bool pnpoly(vec2 p) {
void main(void) {
if (points_in_count > 0 && pnpoly(ove_texcoord * resolution_in)) {
fragColor = color_in;
gl_FragColor = color_in;
} else {
fragColor = vec4(0.0, 0.0, 0.0, 0.0);
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
}
+5 -7
View File
@@ -2,9 +2,7 @@ uniform sampler2D ove_maintex;
uniform vec2 viewport;
uniform float histogram_scale;
in vec2 ove_texcoord;
out vec4 fragColor;
varying vec2 ove_texcoord;
void main(void) {
float histogram_width = ceil(histogram_scale * viewport.y);
@@ -13,9 +11,9 @@ void main(void) {
vec3 sum = vec3(0.0);
float ratio = 0.0;
for (int i = 0; i < histogram_width; i++) {
ratio = float(i) / float(histogram_width - 1);
cur_col = texture(
for (int i = 0; float(i) < histogram_width; i++) {
ratio = float(i) / float(histogram_width - 1.0);
cur_col = texture2D(
ove_maintex,
vec2(ove_texcoord.y, ratio)
).rgb;
@@ -29,5 +27,5 @@ void main(void) {
);
}
fragColor = vec4(sum, 1.0);
gl_FragColor = vec4(sum, 1.0);
}
+3 -3
View File
@@ -1,9 +1,9 @@
uniform float histogram_scale;
in vec4 a_position;
in vec2 a_texcoord;
attribute vec4 a_position;
attribute vec2 a_texcoord;
out vec2 ove_texcoord;
varying vec2 ove_texcoord;
mat4 scale_mat4(vec3 scale) {
return mat4(
+4 -6
View File
@@ -4,9 +4,7 @@ uniform vec2 viewport;
uniform float histogram_scale;
uniform float histogram_power;
in vec2 ove_texcoord;
out vec4 fragColor;
varying vec2 ove_texcoord;
void main(void) {
vec3 col = vec3(0.0);
@@ -17,9 +15,9 @@ void main(void) {
vec3 total_pixels = vec3(ceil(viewport.x * viewport.y *
histogram_scale));
for (int i = 0; i < histogram_height; i++) {
for (int i = 0; float(i) < histogram_height; i++) {
ratio = float(i) / float(histogram_height - 1.0);
sum += texture(
sum += texture2D(
ove_maintex,
vec2(ove_texcoord.x, ratio)
).rgb;
@@ -28,5 +26,5 @@ void main(void) {
histogram_ratio = pow(sum / total_pixels, vec3(histogram_power));
col = step(vec3(ove_texcoord.y), histogram_ratio);
fragColor = vec4(col, 1.0);
gl_FragColor = vec4(col, 1.0);
}
+4 -6
View File
@@ -5,9 +5,7 @@ uniform vec3 luma_coeffs;
uniform float waveform_scale;
in vec2 ove_texcoord;
out vec4 fragColor;
varying vec2 ove_texcoord;
void main(void) {
float waveform_height = ceil(waveform_scale * viewport.y);
@@ -17,9 +15,9 @@ void main(void) {
vec4 cur_col = vec4(0.0);
float ratio = 0.0;
for (int i = 0; i < waveform_height; i++) {
for (int i = 0; float(i) < waveform_height; i++) {
ratio = float(i) / float(waveform_height - 1.0);
cur_col.rgb = texture(
cur_col.rgb = texture2D(
ove_maintex,
vec2(ove_texcoord.x, ratio)
).rgb;
@@ -35,5 +33,5 @@ void main(void) {
}
col.rgb += vec3(col.w);
fragColor = vec4(col.rgb, 1.0);
gl_FragColor = vec4(col.rgb, 1.0);
}
+3 -3
View File
@@ -1,9 +1,9 @@
uniform float waveform_scale;
in vec4 a_position;
in vec2 a_texcoord;
attribute vec4 a_position;
attribute vec2 a_texcoord;
out vec2 ove_texcoord;
varying vec2 ove_texcoord;
mat4 scale_mat4(vec3 scale) {
return mat4(
+1 -3
View File
@@ -1,7 +1,5 @@
uniform vec4 color_in;
out vec4 fragColor;
void main(void) {
fragColor = color_in;
gl_FragColor = color_in;
}
+5 -7
View File
@@ -9,12 +9,10 @@ uniform vec2 resolution_in;
// Standard inputs
uniform int ove_iteration;
in vec2 ove_texcoord;
out vec4 fragColor;
varying vec2 ove_texcoord;
void main(void) {
vec4 pixel_here = texture(tex_in, ove_texcoord);
vec4 pixel_here = texture2D(tex_in, ove_texcoord);
// Detect no-op situations
if (radius_in == 0.0
@@ -22,7 +20,7 @@ void main(void) {
|| (inner_in && pixel_here.a == 0.0)
|| (!inner_in && pixel_here.a == 1.0)) {
// No-op, do nothing
fragColor = pixel_here;
gl_FragColor = pixel_here;
return;
}
@@ -39,7 +37,7 @@ void main(void) {
if (abs(length(vec2(i, j))) < radius) {
// Get pixel here
float alpha = texture(tex_in, ove_texcoord + vec2(x_coord, y_coord)).a;
float alpha = texture2D(tex_in, ove_texcoord + vec2(x_coord, y_coord)).a;
if (inner_in) {
alpha = 1.0 - alpha;
@@ -76,5 +74,5 @@ void main(void) {
stroke_col = stroke_col * (1.0 - pixel_here.a) + pixel_here;
}
fragColor = stroke_col;
gl_FragColor = stroke_col;
}
+11 -2
View File
@@ -60,10 +60,19 @@ void ColorPreviewBox::paintEvent(QPaintEvent *e)
QPainter p(this);
QRect draw_rect = rect().adjusted(0, 0, -1, -1);
p.setPen(Qt::black);
if (color_.alpha() < 1.0) {
// Draw black background so the background isn't the window color
p.setBrush(Qt::black);
p.drawRect(draw_rect);
}
// Draw with color over the top
p.setBrush(c);
p.drawRect(rect().adjusted(0, 0, -1, -1));
p.drawRect(draw_rect);
}
}
+9 -1
View File
@@ -27,7 +27,13 @@ namespace olive {
PixelSamplerWidget::PixelSamplerWidget(QWidget *parent) :
QGroupBox(parent)
{
QVBoxLayout* layout = new QVBoxLayout(this);
QHBoxLayout* layout = new QHBoxLayout(this);
box_ = new ColorPreviewBox();
QFontMetrics fm = fontMetrics();
int box_sz = fm.height() * 2;
box_->setFixedSize(box_sz, box_sz);
layout->addWidget(box_);
label_ = new QLabel();
layout->addWidget(label_);
@@ -45,6 +51,8 @@ void PixelSamplerWidget::SetValues(const Color &color)
void PixelSamplerWidget::UpdateLabelInternal()
{
box_->SetColor(color_);
label_->setText(tr("<html>"
"<font color='#FF8080'>R: %1</font><br>"
"<font color='#80FF80'>G: %2</font><br>"
+3
View File
@@ -26,6 +26,7 @@
#include <QWidget>
#include "render/color.h"
#include "widget/colorwheel/colorpreviewbox.h"
namespace olive {
@@ -43,6 +44,8 @@ private:
Color color_;
ColorPreviewBox *box_;
QLabel* label_;
};
+11 -38
View File
@@ -28,23 +28,22 @@ namespace olive {
ScopeBase::ScopeBase(QWidget* parent) :
super(parent),
buffer_(nullptr)
texture_(nullptr),
managed_tex_up_to_date_(false)
{
EnableDefaultContextMenu();
}
void ScopeBase::SetBuffer(Frame *frame)
void ScopeBase::SetBuffer(TexturePtr frame)
{
buffer_ = frame;
UploadTextureFromBuffer();
texture_ = frame;
managed_tex_up_to_date_ = false;
update();
}
void ScopeBase::showEvent(QShowEvent* e)
{
super::showEvent(e);
UploadTextureFromBuffer();
}
void ScopeBase::DrawScope(TexturePtr managed_tex, QVariant pipeline)
@@ -58,32 +57,6 @@ void ScopeBase::DrawScope(TexturePtr managed_tex, QVariant pipeline)
VideoParams::kInternalChannelCount));
}
void ScopeBase::UploadTextureFromBuffer()
{
if (!isVisible()) {
return;
}
if (buffer_) {
makeCurrent();
if (!texture_ || texture_->params() != buffer_->video_params()) {
texture_ = nullptr;
managed_tex_ = nullptr;
texture_ = renderer()->CreateTexture(buffer_->video_params(),
buffer_->data(), buffer_->linesize_pixels());
managed_tex_ = renderer()->CreateTexture(buffer_->video_params());
} else {
texture_->Upload(buffer_->data(), buffer_->linesize_pixels());
}
doneCurrent();
}
update();
}
void ScopeBase::OnInit()
{
super::OnInit();
@@ -96,13 +69,13 @@ void ScopeBase::OnPaint()
// Clear display surface
renderer()->ClearDestination();
if (buffer_) {
if (texture_) {
// Convert reference frame to display space
if (!texture_ || !managed_tex_) {
UploadTextureFromBuffer();
makeCurrent(); // UploadTextureFromBuffer calls "doneCurrent", so we re-call "makeCurrent"
if (!managed_tex_ || !managed_tex_up_to_date_
|| managed_tex_->params() != texture_->params()) {
managed_tex_ = renderer()->CreateTexture(texture_->params());
renderer()->BlitColorManaged(color_service(), texture_, true, managed_tex_.get());
}
renderer()->BlitColorManaged(color_service(), texture_, true, managed_tex_.get());
DrawScope(managed_tex_, pipeline_);
}
+2 -4
View File
@@ -35,7 +35,7 @@ public:
MANAGEDDISPLAYWIDGET_DEFAULT_DESTRUCTOR(ScopeBase)
public slots:
void SetBuffer(Frame* frame);
void SetBuffer(TexturePtr frame);
protected slots:
virtual void OnInit() override;
@@ -57,15 +57,13 @@ protected:
virtual void DrawScope(TexturePtr managed_tex, QVariant pipeline);
private:
void UploadTextureFromBuffer();
QVariant pipeline_;
TexturePtr texture_;
TexturePtr managed_tex_;
Frame* buffer_;
bool managed_tex_up_to_date_;
};
+22 -8
View File
@@ -463,7 +463,6 @@ public:
Q_ASSERT(i == 0 || time > times_.at(i-1));
QVector<Block*> splits(blocks_.size());
commands_.resize(blocks_.size());
for (int j=0;j<blocks_.size();j++) {
Block* b = blocks_.at(j);
@@ -472,7 +471,7 @@ public:
BlockSplitCommand* split_command = new BlockSplitCommand(b, time);
split_command->redo();
splits.replace(j, split_command->new_block());
commands_.replace(j, split_command);
commands_.append(split_command);
} else {
splits.replace(j, nullptr);
}
@@ -2045,8 +2044,8 @@ public:
}
foreach (auto add_gap, gaps_added_) {
add_gap.gap->setParent(add_gap.before->parent());
add_gap.before->track()->InsertBlockAfter(add_gap.gap, add_gap.before);
add_gap.gap->setParent(add_gap.track->parent());
add_gap.track->InsertBlockAfter(add_gap.gap, add_gap.before);
}
foreach (Track* track, working_tracks_) {
@@ -2121,6 +2120,7 @@ private:
QVector<Block*> blocks_to_split;
QVector<Block*> blocks_to_append_gap_to;
QVector<Track*> tracks_to_append_gap_to;
foreach (Track* track, working_tracks_) {
foreach (Block* b, track->Blocks()) {
@@ -2129,11 +2129,24 @@ private:
gaps_to_extend_.append(b);
break;
} else if (dynamic_cast<ClipBlock*>(b) && b->out() >= point_) {
if (b->out() > point_) {
bool append_gap = true;
if (b->in() == point_) {
// The only reason we should be here is if this block is at the start of the track,
// in which case no split needs to occur
b = nullptr;
} else if (b->out() > point_) {
// Block must be split as well as having a gap appended to it
blocks_to_split.append(b);
} else if (!b->next()) {
// At the end of a track, no gap needs to be added at all
append_gap = false;
}
blocks_to_append_gap_to.append(b);
if (append_gap) {
tracks_to_append_gap_to.append(track);
blocks_to_append_gap_to.append(b);
}
break;
}
}
@@ -2143,11 +2156,11 @@ private:
split_command_ = new BlockSplitPreservingLinksCommand(blocks_to_split, {point_});
}
foreach (Block* block, blocks_to_append_gap_to) {
for (int i=0; i<blocks_to_append_gap_to.size(); i++) {
GapBlock* gap = new GapBlock();
gap->set_length_and_media_out(length_);
gap->setParent(&memory_manager_);
gaps_added_.append({gap, block});
gaps_added_.append({gap, blocks_to_append_gap_to.at(i), tracks_to_append_gap_to.at(i)});
}
}
@@ -2166,6 +2179,7 @@ private:
struct AddGap {
GapBlock* gap;
Block* before;
Track* track;
};
QVector<AddGap> gaps_added_;
+15 -41
View File
@@ -56,8 +56,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
color_menu_enabled_(true),
time_changed_from_timer_(false),
prequeuing_(false),
last_loaded_buffer_(nullptr),
last_loaded_buffer_is_empty_(false),
active_queue_jobs_(0),
cache_time_(rational::NaN)
{
@@ -83,6 +81,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
connect(display_widget_, &ViewerDisplayWidget::DragEntered, this, &ViewerWidget::DragEntered);
connect(display_widget_, &ViewerDisplayWidget::Dropped, this, &ViewerWidget::Dropped);
connect(display_widget_, &ViewerDisplayWidget::VisibilityChanged, this, &ViewerWidget::Pause);
connect(display_widget_, &ViewerDisplayWidget::TextureChanged, this, &ViewerWidget::TextureChanged);
connect(sizer_, &ViewerSizer::RequestScale, display_widget_, &ViewerDisplayWidget::SetMatrixZoom);
connect(sizer_, &ViewerSizer::RequestTranslate, display_widget_, &ViewerDisplayWidget::SetMatrixTranslate);
connect(display_widget_, &ViewerDisplayWidget::HandDragMoved, sizer_, &ViewerSizer::HandDragMove);
@@ -249,7 +248,7 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n)
disconnect(n->audio_playback_cache(), &AudioPlaybackCache::Validated, this, &ViewerWidget::AudioCacheValidated);
disconnect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack);
SetDisplayImage(nullptr);
SetDisplayImage(QVariant());
ruler()->SetPlaybackCache(nullptr);
@@ -351,7 +350,7 @@ void ViewerWidget::SetFullScreen(QScreen *screen)
vw->display_widget()->SetDeinterlacing(vw->display_widget()->IsDeinterlacing());
}
vw->display_widget()->SetImage(last_loaded_buffer_);
vw->display_widget()->SetImage(QVariant::fromValue(display_widget()->GetCurrentTexture()));
windows_.insert(screen, vw);
}
@@ -420,33 +419,10 @@ bool ViewerWidget::ShouldForceWaveform() const
void ViewerWidget::SetEmptyImage()
{
FramePtr frame = nullptr;
display_widget()->SetBlank();
if (GetConnectedNode()) {
frame = last_loaded_buffer_;
if (!frame) {
frame = Frame::Create();
}
if (frame->video_params() != GetConnectedNode()->GetVideoParams()) {
frame->destroy();
frame->set_video_params(GetConnectedNode()->GetVideoParams());
}
if (!frame->is_allocated()) {
frame->allocate();
}
if (!last_loaded_buffer_is_empty_) {
memset(frame->data(), 0, frame->allocated_size());
}
}
SetDisplayImage(frame);
if (frame) {
last_loaded_buffer_is_empty_ = true;
foreach (ViewerWindow *vw, windows_) {
vw->display_widget()->SetBlank();
}
}
@@ -554,6 +530,7 @@ void ViewerWidget::UpdateTextureFromNode()
} else {
// Not playing, run a task to get the frame either from the cache or the renderer
RenderTicketWatcher* watcher = new RenderTicketWatcher();
watcher->setProperty("time", QVariant::fromValue(time));
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrame);
nonqueue_watchers_.append(watcher);
watcher->SetTicket(GetFrame(time, true));
@@ -723,7 +700,7 @@ bool ViewerWidget::ViewerMightBeAStill()
return GetConnectedNode() && GetConnectedNode()->GetConnectedTextureOutput().IsValid() && GetConnectedNode()->GetVideoLength().isNull();
}
void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only)
void ViewerWidget::SetDisplayImage(QVariant frame, bool main_only)
{
display_widget_->SetImage(frame);
@@ -732,10 +709,6 @@ void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only)
vw->display_widget()->SetImage(frame);
}
}
last_loaded_buffer_ = frame;
last_loaded_buffer_is_empty_ = false;
emit LoadedBuffer(frame.get());
}
void ViewerWidget::RequestNextFrameForQueue(bool prioritize, bool increment)
@@ -749,6 +722,7 @@ void ViewerWidget::RequestNextFrameForQueue(bool prioritize, bool increment)
}
RenderTicketWatcher* watcher = new RenderTicketWatcher();
watcher->setProperty("time", QVariant::fromValue(next_time));
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrameForQueue);
watcher->SetTicket(GetFrame(next_time, prioritize));
active_queue_jobs_++;
@@ -886,8 +860,6 @@ void ViewerWidget::RendererGeneratedFrame()
RenderTicketWatcher* ticket = static_cast<RenderTicketWatcher*>(sender());
if (ticket->HasResult()) {
FramePtr frame = ticket->Get().value<FramePtr>();
if (nonqueue_watchers_.contains(ticket)) {
while (!nonqueue_watchers_.isEmpty()) {
if (nonqueue_watchers_.takeFirst() == ticket) {
@@ -895,7 +867,7 @@ void ViewerWidget::RendererGeneratedFrame()
}
}
SetDisplayImage(frame);
SetDisplayImage(ticket->Get());
}
}
@@ -907,14 +879,16 @@ void ViewerWidget::RendererGeneratedFrameForQueue()
RenderTicketWatcher* watcher = static_cast<RenderTicketWatcher*>(sender());
if (watcher->HasResult()) {
FramePtr frame = watcher->Get().value<FramePtr>();
QVariant frame = watcher->Get();
// Ignore this signal if we've paused now
if (IsPlaying() || prequeuing_) {
playback_queue_.AppendTimewise({frame->timestamp(), frame}, playback_speed_);
rational ts = watcher->property("time").value<rational>();
playback_queue_.AppendTimewise({ts, frame}, playback_speed_);
foreach (ViewerWindow* window, windows_) {
window->queue()->AppendTimewise({frame->timestamp(), frame}, playback_speed_);
window->queue()->AppendTimewise({ts, frame}, playback_speed_);
}
if (prequeuing_ && int(playback_queue_.size()) == prequeue_length_) {
+2 -5
View File
@@ -128,7 +128,7 @@ signals:
/**
* @brief Signal emitted when a new frame is loaded
*/
void LoadedBuffer(Frame* load_buffer);
void TextureChanged(TexturePtr t);
/**
* @brief Request a scope panel
@@ -185,7 +185,7 @@ private:
bool ViewerMightBeAStill();
void SetDisplayImage(FramePtr frame, bool main_only = false);
void SetDisplayImage(QVariant frame, bool main_only = false);
void RequestNextFrameForQueue(bool prioritize = false, bool increment = true);
@@ -251,9 +251,6 @@ private:
QTimer audio_restart_timer_;
FramePtr last_loaded_buffer_;
bool last_loaded_buffer_is_empty_;
int active_queue_jobs_;
rational cache_time_;
+84 -47
View File
@@ -45,13 +45,12 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
signal_cursor_color_(false),
gizmos_(nullptr),
gizmo_click_(false),
last_loaded_buffer_(nullptr),
hand_dragging_(false),
deinterlace_(false),
show_fps_(false),
frames_skipped_(0),
show_widget_background_(false),
texture_equal_to_frame_(false)
push_mode_(kPushNull)
{
connect(Core::instance(), &Core::ToolChanged, this, &ViewerDisplayWidget::UpdateCursor);
@@ -100,17 +99,26 @@ void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e)
inner_widget()->setMouseTracking(e);
}
void ViewerDisplayWidget::SetImage(FramePtr in_buffer)
void ViewerDisplayWidget::SetImage(const QVariant &buffer)
{
if (last_loaded_buffer_ != in_buffer) {
last_loaded_buffer_ = in_buffer;
load_frame_ = buffer;
texture_equal_to_frame_ = false;
if (load_frame_.isNull()) {
push_mode_ = kPushNull;
} else {
push_mode_ = kPushFrame;
}
update();
}
void ViewerDisplayWidget::SetBlank()
{
push_mode_ = kPushBlank;
update();
}
void ViewerDisplayWidget::SetDeinterlacing(bool e)
{
deinterlace_ = e;
@@ -318,51 +326,73 @@ void ViewerDisplayWidget::OnPaint()
{
// Clear background to empty
QColor bg_color = show_widget_background_ ? palette().window().color() : Qt::black;
renderer()->ClearDestination(bg_color.redF(), bg_color.greenF(), bg_color.blueF());
renderer()->ClearDestination(nullptr, bg_color.redF(), bg_color.greenF(), bg_color.blueF());
// We only draw if we have a pipeline
if (last_loaded_buffer_ && color_service()) {
if (!texture_
|| texture_->width() != last_loaded_buffer_->width()
|| texture_->height() != last_loaded_buffer_->height()
|| texture_->format() != last_loaded_buffer_->format()
|| texture_->channel_count() != last_loaded_buffer_->channel_count()) {
texture_ = renderer()->CreateTexture(last_loaded_buffer_->video_params(), last_loaded_buffer_->data(), last_loaded_buffer_->linesize_pixels());
} else if (!texture_equal_to_frame_) {
texture_->Upload(last_loaded_buffer_->data(), last_loaded_buffer_->linesize_pixels());
}
texture_equal_to_frame_ = true;
TexturePtr texture_to_draw = texture_;
if (deinterlace_) {
if (deinterlace_shader_.isNull()) {
deinterlace_shader_ = renderer()->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/deinterlace.frag"))));
}
if (!deinterlace_texture_
|| deinterlace_texture_->params() != texture_to_draw->params()) {
// (Re)create texture
deinterlace_texture_ = renderer()->CreateTexture(texture_to_draw->params());
}
ShaderJob job;
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture_to_draw->width(), texture_to_draw->height())));
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_to_draw)));
renderer()->BlitToTexture(deinterlace_shader_, job, deinterlace_texture_.get());
texture_to_draw = deinterlace_texture_;
}
if (push_mode_ != kPushNull) {
// Draw texture through color transform
int device_width = width() * devicePixelRatioF();
int device_height = height() * devicePixelRatioF();
VideoParams::Format device_format = static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt());
VideoParams device_params(device_width, device_height, device_format, VideoParams::kInternalChannelCount);
renderer()->BlitColorManaged(color_service(), texture_to_draw, true, device_params, false,
combined_matrix_flipped_, crop_matrix_);
if (push_mode_ == kPushBlank) {
if (blank_shader_.isNull()) {
blank_shader_ = renderer()->CreateNativeShader(ShaderCode());
}
ShaderJob job;
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, combined_matrix_flipped_));
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix_));
renderer()->Blit(blank_shader_, job, device_params, false);
} else if (color_service()) {
if (FramePtr frame = load_frame_.value<FramePtr>()) {
// This is a CPU frame, upload it now
if (!texture_
|| texture_->renderer() != renderer() // Some implementations don't like it if we upload to a texture created in another (albeit shared) context
|| texture_->width() != frame->width()
|| texture_->height() != frame->height()
|| texture_->format() != frame->format()
|| texture_->channel_count() != frame->channel_count()) {
texture_ = renderer()->CreateTexture(frame->video_params(), frame->data(), frame->linesize_pixels());
} else {
texture_->Upload(frame->data(), frame->linesize_pixels());
}
} else if (TexturePtr texture = load_frame_.value<TexturePtr>()) {
// This is a GPU texture, switch to it directly
texture_ = texture;
}
emit TextureChanged(texture_);
push_mode_ = kPushUnnecessary;
TexturePtr texture_to_draw = texture_;
if (deinterlace_) {
if (deinterlace_shader_.isNull()) {
deinterlace_shader_ = renderer()->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/deinterlace.frag"))));
}
if (!deinterlace_texture_
|| deinterlace_texture_->params() != texture_to_draw->params()) {
// (Re)create texture
deinterlace_texture_ = renderer()->CreateTexture(texture_to_draw->params());
}
ShaderJob job;
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture_to_draw->width(), texture_to_draw->height())));
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_to_draw)));
renderer()->BlitToTexture(deinterlace_shader_, job, deinterlace_texture_.get());
texture_to_draw = deinterlace_texture_;
}
renderer()->BlitColorManaged(color_service(), texture_to_draw, true, device_params, false,
combined_matrix_flipped_, crop_matrix_);
}
}
// Draw gizmos if we have any
@@ -452,12 +482,20 @@ void ViewerDisplayWidget::OnPaint()
void ViewerDisplayWidget::OnDestroy()
{
renderer()->DestroyNativeShader(deinterlace_shader_);
deinterlace_shader_.clear();
renderer()->DestroyNativeShader(blank_shader_);
blank_shader_.clear();
super::OnDestroy();
texture_ = nullptr;
deinterlace_texture_ = nullptr;
if (load_frame_.isNull()) {
push_mode_ = kPushNull;
} else {
push_mode_ = kPushFrame;
}
}
QPointF ViewerDisplayWidget::GetTexturePosition(const QPoint &screen_pos)
@@ -542,12 +580,11 @@ void ViewerDisplayWidget::EmitColorAtCursor(QMouseEvent *e)
if (signal_cursor_color_) {
Color reference, display;
if (last_loaded_buffer_) {
if (texture_) {
QPointF pixel_pos = GenerateGizmoTransform().inverted().map(e->pos());
pixel_pos /= texture_->params().divider();
pixel_pos /= last_loaded_buffer_->video_params().divider();
reference = last_loaded_buffer_->get_pixel(qRound(pixel_pos.x()), qRound(pixel_pos.y()));
reference = renderer()->GetPixelFromTexture(texture_.get(), pixel_pos);
display = color_service()->ConvertColor(reference);
}
+32 -10
View File
@@ -102,6 +102,11 @@ public:
fps_timer_update_count_++;
}
TexturePtr GetCurrentTexture() const
{
return texture_;
}
public slots:
/**
* @brief Set the transformation matrix to draw with
@@ -126,13 +131,9 @@ public slots:
*/
void SetSignalCursorColorEnabled(bool e);
/**
* @brief Overrides the image with the load buffer of another ViewerGLWidget
*
* If there are multiple ViewerGLWidgets showing the same thing, this is faster than decoding the image from file
* each time.
*/
void SetImage(FramePtr in_buffer);
void SetImage(const QVariant &buffer);
void SetBlank();
/**
* @brief Changes the pointer type if the tool is changed to the hand tool. Otherwise resets the pointer to it's
@@ -181,6 +182,8 @@ signals:
void VisibilityChanged(bool visible);
void TextureChanged(TexturePtr texture);
protected:
/**
* @brief Override the mouse press event for the DragStarted() signal and gizmos
@@ -249,6 +252,11 @@ private:
*/
QVariant deinterlace_shader_;
/**
* @brief Blank shader
*/
QVariant blank_shader_;
/**
* @brief Translation only matrix (defaults to identity).
*/
@@ -283,8 +291,6 @@ private:
rational time_;
FramePtr last_loaded_buffer_;
/**
* @brief Position of mouse to calculate delta from.
*/
@@ -304,7 +310,23 @@ private:
bool show_widget_background_;
bool texture_equal_to_frame_;
QVariant load_frame_;
enum PushMode {
/// New frame to push to internal texture
kPushFrame,
/// Internal texture reference is up to date, keep showing it
kPushUnnecessary,
/// Draw blank/black screen
kPushBlank,
/// Draw nothing (not even a black frame)
kPushNull,
};
PushMode push_mode_;
private slots:
void EmitColorAtCursor(QMouseEvent* e);
+1 -1
View File
@@ -29,7 +29,7 @@ namespace olive {
struct ViewerPlaybackFrame {
rational timestamp;
FramePtr frame;
QVariant frame;
};
class ViewerQueue : public std::list<ViewerPlaybackFrame> {
+108
View File
@@ -488,4 +488,112 @@ OLIVE_ADD_TEST(ReplaceBlockWithGap_ClipsAndTransitions)
OLIVE_TEST_END;
}
OLIVE_ADD_TEST(InsertGaps_SingleTrack)
{
TIMELINE_TEST_START;
sequence.add_default_nodes();
TrackList *list = sequence.track_list(Track::kVideo);
Track *track = list->GetTracks().first();
ClipBlock *a = new ClipBlock();
a->setParent(&project);
track->AppendBlock(a);
ClipBlock *b = new ClipBlock();
b->setParent(&project);
track->AppendBlock(b);
ClipBlock *c = new ClipBlock();
c->setParent(&project);
track->AppendBlock(c);
OLIVE_ASSERT(track->Blocks().size() == 3);
OLIVE_ASSERT(track->Blocks().at(0) == a);
OLIVE_ASSERT(track->Blocks().at(1) == b);
OLIVE_ASSERT(track->Blocks().at(2) == c);
{
// Insert gap at the start of the track, all blocks should be unsplit and shifted to the right
TrackListInsertGaps command(list, 0, 2);
command.redo();
OLIVE_ASSERT(track->Blocks().size() == 4);
OLIVE_ASSERT(dynamic_cast<GapBlock *>(track->Blocks().at(0)));
OLIVE_ASSERT(track->Blocks().at(0)->length() == 2);
OLIVE_ASSERT(track->Blocks().at(1) == a);
OLIVE_ASSERT(track->Blocks().at(2) == b);
OLIVE_ASSERT(track->Blocks().at(3) == c);
command.undo();
OLIVE_ASSERT(track->Blocks().size() == 3);
OLIVE_ASSERT(track->Blocks().at(0) == a);
OLIVE_ASSERT(track->Blocks().at(1) == b);
OLIVE_ASSERT(track->Blocks().at(2) == c);
}
{
// Insert gap in the middle of block A, block A should be halved with a copy at 2 and the gap at 1
TrackListInsertGaps command(list, rational(1, 2), 2);
command.redo();
OLIVE_ASSERT(track->Blocks().size() == 5);
OLIVE_ASSERT(track->Blocks().at(0) == a);
OLIVE_ASSERT(track->Blocks().at(0)->length() == rational(1, 2));
OLIVE_ASSERT(dynamic_cast<GapBlock *>(track->Blocks().at(1)));
OLIVE_ASSERT(dynamic_cast<ClipBlock*>(track->Blocks().at(2)));
OLIVE_ASSERT(track->Blocks().at(3) == b);
OLIVE_ASSERT(track->Blocks().at(4) == c);
command.undo();
OLIVE_ASSERT(track->Blocks().size() == 3);
OLIVE_ASSERT(track->Blocks().at(0) == a);
OLIVE_ASSERT(track->Blocks().at(0)->length() = 1);
OLIVE_ASSERT(track->Blocks().at(1) == b);
OLIVE_ASSERT(track->Blocks().at(2) == c);
}
{
// Insert gap between block A and B, blocks should be unsplit with a gap at 1
TrackListInsertGaps command(list, 1, 2);
command.redo();
OLIVE_ASSERT(track->Blocks().size() == 4);
OLIVE_ASSERT(track->Blocks().at(0) == a);
OLIVE_ASSERT(dynamic_cast<GapBlock *>(track->Blocks().at(1)));
OLIVE_ASSERT(track->Blocks().at(2) == b);
OLIVE_ASSERT(track->Blocks().at(3) == c);
command.undo();
OLIVE_ASSERT(track->Blocks().size() == 3);
OLIVE_ASSERT(track->Blocks().at(0) == a);
OLIVE_ASSERT(track->Blocks().at(1) == b);
OLIVE_ASSERT(track->Blocks().at(2) == c);
}
{
// Insert gap at end, nothing should be added
TrackListInsertGaps command(list, 3, 2);
command.redo();
OLIVE_ASSERT(track->Blocks().size() == 3);
OLIVE_ASSERT(track->Blocks().at(0) == a);
OLIVE_ASSERT(track->Blocks().at(1) == b);
OLIVE_ASSERT(track->Blocks().at(2) == c);
command.undo();
OLIVE_ASSERT(track->Blocks().size() == 3);
OLIVE_ASSERT(track->Blocks().at(0) == a);
OLIVE_ASSERT(track->Blocks().at(1) == b);
OLIVE_ASSERT(track->Blocks().at(2) == c);
}
OLIVE_TEST_END;
}
}