Merge branch 'master' into nodeview-redux
This commit is contained in:
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
static QString TypeToName(Type t);
|
||||
|
||||
public slots:
|
||||
void SetReferenceBuffer(Frame* frame);
|
||||
void SetReferenceBuffer(TexturePtr frame);
|
||||
|
||||
void SetColorManager(ColorManager* manager);
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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, ¤t_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, ¤t_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 ¶ms, 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);
|
||||
|
||||
@@ -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 ¶ms, const void* data = nullptr, int linesize = 0);
|
||||
|
||||
GLuint GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count);
|
||||
|
||||
QTimer cache_timer_;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
#include "common/ocioutils.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -42,11 +44,7 @@ TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, 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 ¶ms, 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 ¶ms)
|
||||
{
|
||||
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 ¶ms, 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
@@ -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 ¶ms);
|
||||
|
||||
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 ¶ms, Texture::Type type = Texture::k2D);
|
||||
|
||||
private:
|
||||
struct ColorContext {
|
||||
struct LUT {
|
||||
@@ -128,6 +137,8 @@ private:
|
||||
|
||||
QMutex color_cache_mutex_;
|
||||
|
||||
QVariant interlace_texture_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -120,6 +120,11 @@ public:
|
||||
return type_;
|
||||
}
|
||||
|
||||
Renderer* renderer() const
|
||||
{
|
||||
return renderer_;
|
||||
}
|
||||
|
||||
private:
|
||||
Renderer* renderer_;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,7 +1,5 @@
|
||||
uniform vec4 color_in;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main(void) {
|
||||
fragColor = color_in;
|
||||
gl_FragColor = color_in;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>"
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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_) {
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace olive {
|
||||
|
||||
struct ViewerPlaybackFrame {
|
||||
rational timestamp;
|
||||
FramePtr frame;
|
||||
QVariant frame;
|
||||
};
|
||||
|
||||
class ViewerQueue : public std::list<ViewerPlaybackFrame> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user