diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index c682fb8a6..577c7d15d 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -137,6 +137,7 @@ void ViewerOutput::set_video_params(const VideoParams &video) bool size_changed = video_params_.width() != video.width() || video_params_.height() != video.height(); bool timebase_changed = video_params_.time_base() != video.time_base(); bool pixel_aspect_changed = video_params_.pixel_aspect_ratio() != video.pixel_aspect_ratio(); + bool interlacing_changed = video_params_.interlacing() != video.interlacing(); video_params_ = video; @@ -148,6 +149,10 @@ void ViewerOutput::set_video_params(const VideoParams &video) emit PixelAspectChanged(video_params_.pixel_aspect_ratio()); } + if (interlacing_changed) { + emit InterlacingChanged(video_params_.interlacing()); + } + if (timebase_changed) { video_frame_cache_.SetTimebase(video_params_.time_base()); emit TimebaseChanged(video_params_.time_base()); diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 2d67c9800..96c30cdf9 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -133,6 +133,8 @@ signals: void PixelAspectChanged(const rational& pixel_aspect); + void InterlacingChanged(VideoParams::Interlacing mode); + void VideoParamsChanged(); void AudioParamsChanged(); diff --git a/app/render/backend/opengl/openglshader.cpp b/app/render/backend/opengl/openglshader.cpp index ff75ad189..7e76f619a 100644 --- a/app/render/backend/opengl/openglshader.cpp +++ b/app/render/backend/opengl/openglshader.cpp @@ -146,8 +146,9 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx, return shader; } -QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QString &shader_code) +QString OpenGLShader::CodeDefaultFragment(QString function_name, const QString &shader_code) { + // Create shader header QString frag_code = QStringLiteral("#version 150\n" "\n" "#ifdef GL_ES\n" @@ -156,48 +157,47 @@ QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QS "#endif\n" "\n" "uniform sampler2D ove_maintex;\n" - "uniform bool color_only;\n" - "uniform vec4 color_only_color;\n" + "uniform vec2 ove_resolution;\n" + "uniform bool ove_deinterlace;\n" "\n" "in vec2 ove_texcoord;\n" "\n" "out vec4 fragColor;\n" "\n"); - // Finish the function with the main function - // Check if additional code was passed to this function, add it here - if (shader_code.isEmpty()) { - - // If not, just add a pure main() function - - frag_code.append(QStringLiteral("\n" - "void main() {\n" - " if (color_only) {\n" - " fragColor = color_only_color;" - " } else {\n" - " vec4 color = texture(ove_maintex, ove_texcoord);\n" - " fragColor = color;\n" - " }\n" - "}\n")); - - } else { + if (!function_name.isEmpty() && !shader_code.isEmpty()) { // If additional code was passed, add it and reference it in main(). // - // The function in the additional code is expected to be `vec4 function_name(vec4 color)`. The texture coordinate - // can be acquired through `ove_texcoord`. + // The function in the additional code is expected to be `vec4 function_name(vec4 color)`. + // The texture coordinate can be acquired through `ove_texcoord`. frag_code.append(shader_code); - frag_code.append(QStringLiteral("\n" - "void main() {\n" - " vec4 color = %1(texture(ove_maintex, ove_texcoord));\n" - " fragColor = color;\n" - "}\n").arg(function_name)); + } else { + + // No function to call + function_name = QString(); } + // Our function_name arg will either resolve to the function added to this or to nothing, in + // which case they'll just be benign brackets. + frag_code.append(QStringLiteral("\n" + "void main() {\n" + " vec2 using_texcoord = ove_texcoord;\n" + " if (ove_deinterlace) {\n" + " // A very basic deinterlace that halves the vertical\n" + " // resolution and linearly interpolates the two fields\n" + " // by reading the texture coord between them.\n" + " float half_vert = round(ove_resolution.y / 2.0);\n" + " using_texcoord.y = (round(using_texcoord.y * half_vert) + 0.25) / half_vert;\n" + " }\n" + " vec4 color = %1(texture(ove_maintex, using_texcoord));\n" + " fragColor = color;\n" + "}\n").arg(function_name)); + return frag_code; } diff --git a/app/render/backend/opengl/openglshader.h b/app/render/backend/opengl/openglshader.h index 3adab00d4..452dc3c3d 100644 --- a/app/render/backend/opengl/openglshader.h +++ b/app/render/backend/opengl/openglshader.h @@ -51,7 +51,7 @@ public: OCIO::ConstProcessorRcPtr processor, bool alpha_is_associated); - static QString CodeDefaultFragment(const QString &function_name = QString(), + static QString CodeDefaultFragment(QString function_name = QString(), const QString &shader_code = QString()); static QString CodeDefaultVertex(); static QString CodeAlphaDisassociate(const QString& function_name); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 62d42aed4..f6a71f333 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -167,12 +167,15 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) connect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SetViewerResolution); connect(n, &ViewerOutput::PixelAspectChanged, this, &ViewerWidget::SetViewerPixelAspect); connect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); + connect(n, &ViewerOutput::InterlacingChanged, this, &ViewerWidget::InterlacingChangedSlot); connect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererVideoParameters); connect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters); connect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange); connect(n->video_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange); connect(n, &ViewerOutput::GraphChangedFrom, this, &ViewerWidget::UpdateStack); + InterlacingChangedSlot(n->video_params().interlacing()); + ruler()->SetPlaybackCache(n->video_frame_cache()); n->audio_playback_cache()->SetParameters(n->audio_params()); @@ -218,6 +221,7 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) disconnect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SetViewerResolution); disconnect(n, &ViewerOutput::PixelAspectChanged, this, &ViewerWidget::SetViewerPixelAspect); disconnect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); + disconnect(n, &ViewerOutput::InterlacingChanged, this, &ViewerWidget::InterlacingChangedSlot); disconnect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererVideoParameters); disconnect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters); disconnect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange); @@ -824,6 +828,16 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) connect(full_screen_menu, &QMenu::triggered, this, &ViewerWidget::ContextMenuSetFullScreen); } + { + // Deinterlace Option + if (GetConnectedNode()->video_params().interlacing() != VideoParams::kInterlaceNone) { + QAction* deinterlace_action = menu.addAction(tr("Deinterlace")); + deinterlace_action->setCheckable(true); + deinterlace_action->setChecked(display_widget_->IsDeinterlacing()); + connect(deinterlace_action, &QAction::triggered, display_widget_, &ViewerDisplayWidget::SetDeinterlacing); + } + } + menu.addSeparator(); { @@ -1089,6 +1103,14 @@ void ViewerWidget::LengthChangedSlot(const rational &length) } } +void ViewerWidget::InterlacingChangedSlot(VideoParams::Interlacing interlacing) +{ + // Automatically set a "sane" deinterlacing option + display_widget_->SetDeinterlacing(interlacing != VideoParams::kInterlaceNone); + + // FIXME: Set windows too +} + void ViewerWidget::UpdateRendererVideoParameters() { renderer_->ClearVideoQueue(); diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 419aa8c65..5edc2821f 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -261,6 +261,8 @@ private slots: void LengthChangedSlot(const rational& length); + void InterlacingChangedSlot(VideoParams::Interlacing interlacing); + void UpdateRendererVideoParameters(); void UpdateRendererAudioParameters(); diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 943314ef7..7aa6db6fe 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -43,7 +43,8 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : signal_cursor_color_(false), gizmos_(nullptr), gizmo_click_(false), - last_loaded_buffer_(nullptr) + last_loaded_buffer_(nullptr), + deinterlace_(false) { } @@ -86,6 +87,12 @@ void ViewerDisplayWidget::SetImage(FramePtr in_buffer) update(); } +void ViewerDisplayWidget::SetDeinterlacing(bool e) +{ + deinterlace_ = e; + update(); +} + const ViewerSafeMarginInfo &ViewerDisplayWidget::GetSafeMargin() const { return safe_margin_; @@ -212,6 +219,12 @@ void ViewerDisplayWidget::paintGL() // Bind retrieved texture f->glBindTexture(GL_TEXTURE_2D, texture_.texture()); + // Set some parameters + color_service()->pipeline()->bind(); + color_service()->pipeline()->setUniformValue("ove_resolution", texture_.width(), texture_.height()); + color_service()->pipeline()->setUniformValue("ove_deinterlace", deinterlace_); + color_service()->pipeline()->release(); + // Blit using the color service color_service()->ProcessOpenGL(true, matrix_); diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index f8db608ef..7a240beff 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -77,6 +77,11 @@ public: FramePtr last_loaded_buffer() const; + bool IsDeinterlacing() const + { + return deinterlace_; + } + public slots: /** * @brief Set the transformation matrix to draw with @@ -102,6 +107,11 @@ public slots: */ void SetImage(FramePtr in_buffer); + /** + * @brief Enables/disables a basic deinterlace on the viewer + */ + void SetDeinterlacing(bool e); + signals: /** * @brief Signal emitted when the user starts dragging from the viewer @@ -174,6 +184,8 @@ private: FramePtr last_loaded_buffer_; + bool deinterlace_; + private slots: /** * @brief Slot to connect just before the OpenGL context is destroyed to clean up resources