scope: implemented waveform scope
This commit is contained in:
@@ -50,6 +50,9 @@ int main(int argc, char *argv[]) {
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
// Try to share OpenGL contexts
|
||||
QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||
|
||||
// Create application instance
|
||||
QApplication a(argc, argv);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ add_subdirectory(node)
|
||||
add_subdirectory(param)
|
||||
add_subdirectory(pixelsampler)
|
||||
add_subdirectory(project)
|
||||
add_subdirectory(scope)
|
||||
add_subdirectory(sequenceviewer)
|
||||
add_subdirectory(taskmanager)
|
||||
add_subdirectory(timebased)
|
||||
|
||||
@@ -28,7 +28,9 @@ FootageViewerPanel::FootageViewerPanel(QWidget *parent) :
|
||||
ViewerPanelBase(QStringLiteral("FootageViewerPanel"), parent)
|
||||
{
|
||||
// Set ViewerWidget as the central widget
|
||||
SetTimeBasedWidget(new FootageViewerWidget());
|
||||
FootageViewerWidget* fvw = new FootageViewerWidget();
|
||||
connect(fvw, &FootageViewerWidget::RequestScopePanel, this, &FootageViewerPanel::CreateScopePanel);
|
||||
SetTimeBasedWidget(fvw);
|
||||
|
||||
// Set strings
|
||||
Retranslate();
|
||||
|
||||
@@ -40,7 +40,9 @@ ProjectPanel::ProjectPanel(QWidget *parent) :
|
||||
// Create main widget and its layout
|
||||
QWidget* central_widget = new QWidget(this);
|
||||
QVBoxLayout* layout = new QVBoxLayout(central_widget);
|
||||
setWidget(central_widget);
|
||||
layout->setMargin(0);
|
||||
|
||||
SetWidgetWithPadding(central_widget);
|
||||
|
||||
// Set up project toolbar
|
||||
ProjectToolbar* toolbar = new ProjectToolbar(this);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
panel/scope/scope.h
|
||||
panel/scope/scope.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,100 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "scope.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "panel/viewer/viewer.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
ScopePanel::ScopePanel(QWidget* parent) :
|
||||
PanelWidget(QStringLiteral("ScopePanel"), parent)
|
||||
{
|
||||
QWidget* central = new QWidget();
|
||||
setWidget(central);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(central);
|
||||
|
||||
QHBoxLayout* toolbar_layout = new QHBoxLayout();
|
||||
toolbar_layout->setMargin(0);
|
||||
|
||||
scope_type_combobox_ = new QComboBox();
|
||||
|
||||
for (int i=0;i<ScopePanel::kTypeCount;i++) {
|
||||
// These strings get filled in later in Retranslate()
|
||||
scope_type_combobox_->addItem(QString());
|
||||
}
|
||||
|
||||
toolbar_layout->addWidget(scope_type_combobox_);
|
||||
toolbar_layout->addStretch();
|
||||
|
||||
layout->addLayout(toolbar_layout);
|
||||
|
||||
stack_ = new QStackedWidget();
|
||||
layout->addWidget(stack_);
|
||||
|
||||
// Create waveform view
|
||||
waveform_view_ = new WaveformScope();
|
||||
stack_->addWidget(waveform_view_);
|
||||
|
||||
// Create histogram
|
||||
stack_->addWidget(new QWidget());
|
||||
|
||||
connect(scope_type_combobox_, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), stack_, &QStackedWidget::setCurrentIndex);
|
||||
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
void ScopePanel::SetType(ScopePanel::Type t)
|
||||
{
|
||||
scope_type_combobox_->setCurrentIndex(t);
|
||||
}
|
||||
|
||||
QString ScopePanel::TypeToName(ScopePanel::Type t)
|
||||
{
|
||||
switch (t) {
|
||||
case kTypeWaveform:
|
||||
return tr("Waveform");
|
||||
case kTypeHistogram:
|
||||
return tr("Histogram");
|
||||
case kTypeCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void ScopePanel::DrewManagedTexture(OpenGLTexture *texture)
|
||||
{
|
||||
waveform_view_->SetTexture(texture);
|
||||
}
|
||||
|
||||
void ScopePanel::Retranslate()
|
||||
{
|
||||
SetTitle(tr("Scope"));
|
||||
|
||||
for (int i=0;i<ScopePanel::kTypeCount;i++) {
|
||||
scope_type_combobox_->setItemText(i, TypeToName(static_cast<Type>(i)));
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -0,0 +1,70 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SCOPE_PANEL_H
|
||||
#define SCOPE_PANEL_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QStackedWidget>
|
||||
|
||||
#include "widget/panel/panel.h"
|
||||
#include "widget/scope/waveform/waveform.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class ViewerPanel;
|
||||
|
||||
class ScopePanel : public PanelWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Type {
|
||||
kTypeWaveform,
|
||||
kTypeHistogram,
|
||||
|
||||
kTypeCount
|
||||
};
|
||||
|
||||
ScopePanel(QWidget* parent = nullptr);
|
||||
|
||||
void SetType(Type t);
|
||||
|
||||
static QString TypeToName(Type t);
|
||||
|
||||
public slots:
|
||||
void DrewManagedTexture(OpenGLTexture* texture);
|
||||
|
||||
protected:
|
||||
virtual void Retranslate() override;
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
|
||||
QStackedWidget* stack_;
|
||||
|
||||
QComboBox* scope_type_combobox_;
|
||||
|
||||
WaveformScope* waveform_view_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // SCOPE_PANEL_H
|
||||
@@ -70,7 +70,7 @@ void TimeBasedPanel::SetTimebase(const rational &timebase)
|
||||
|
||||
void TimeBasedPanel::SetTime(const int64_t ×tamp)
|
||||
{
|
||||
widget_->SetTime(timestamp);
|
||||
widget_->SetTimestamp(timestamp);
|
||||
}
|
||||
|
||||
void TimeBasedPanel::GoToPrevCut()
|
||||
|
||||
@@ -26,7 +26,9 @@ ViewerPanel::ViewerPanel(const QString &object_name, QWidget *parent) :
|
||||
ViewerPanelBase(object_name, parent)
|
||||
{
|
||||
// Set ViewerWidget as the central widget
|
||||
SetTimeBasedWidget(new ViewerWidget());
|
||||
ViewerWidget* vw = new ViewerWidget();
|
||||
connect(vw, &ViewerWidget::RequestScopePanel, this, &ViewerPanel::CreateScopePanel);
|
||||
SetTimeBasedWidget(vw);
|
||||
|
||||
// Set strings
|
||||
Retranslate();
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
#include "viewerbase.h"
|
||||
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
ViewerPanelBase::ViewerPanelBase(const QString& object_name, QWidget *parent) :
|
||||
TimeBasedPanel(object_name, parent)
|
||||
TimeBasedPanel(object_name, parent),
|
||||
scope_panel_count_(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -88,4 +91,38 @@ void ViewerPanelBase::SetFullScreen(QScreen *screen)
|
||||
static_cast<ViewerWidget*>(GetTimeBasedWidget())->SetFullScreen(screen);
|
||||
}
|
||||
|
||||
void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type)
|
||||
{
|
||||
ViewerWidget* vw = static_cast<ViewerWidget*>(GetTimeBasedWidget());
|
||||
ScopePanel* p = Core::instance()->main_window()->AppendScopePanel();
|
||||
|
||||
p->SetType(type);
|
||||
|
||||
// We treat our scope panels as kind of children, and destroy them if we're ever destroyed
|
||||
connect(this, &ViewerPanelBase::destroyed, p, &ScopePanel::deleteLater);
|
||||
|
||||
// If the scope closes, reduce the count (we do this because if no scopes are open, we can optimize the viewer slightly)
|
||||
connect(p, &ScopePanel::CloseRequested, this, &ViewerPanelBase::ScopePanelClosed);
|
||||
|
||||
// Connect viewer widget texture drawing to scope panel
|
||||
connect(vw, &ViewerWidget::DrewManagedTexture, p, &ScopePanel::DrewManagedTexture);
|
||||
|
||||
if (!scope_panel_count_) {
|
||||
vw->SetEmitDrewManagedTextureEnabled(true);
|
||||
}
|
||||
|
||||
scope_panel_count_++;
|
||||
|
||||
vw->ForceUpdate();
|
||||
}
|
||||
|
||||
void ViewerPanelBase::ScopePanelClosed()
|
||||
{
|
||||
scope_panel_count_--;
|
||||
|
||||
if (!scope_panel_count_) {
|
||||
static_cast<ViewerWidget*>(GetTimeBasedWidget())->SetEmitDrewManagedTextureEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -56,6 +56,15 @@ public:
|
||||
*/
|
||||
void SetFullScreen(QScreen* screen = nullptr);
|
||||
|
||||
protected:
|
||||
void CreateScopePanel(ScopePanel::Type type);
|
||||
|
||||
private:
|
||||
int scope_panel_count_;
|
||||
|
||||
private slots:
|
||||
void ScopePanelClosed();
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Adapted from "RGB Waveform" by lebek
|
||||
// https://www.shadertoy.com/view/4dK3Wc
|
||||
|
||||
#version 110
|
||||
|
||||
uniform sampler2D ove_maintex;
|
||||
uniform vec2 ove_resolution;
|
||||
varying vec2 ove_texcoord;
|
||||
|
||||
uniform float threshold;
|
||||
|
||||
void main(void) {
|
||||
vec3 col = vec3(0.0);
|
||||
float s = ove_texcoord.y*1.8 - 0.15;
|
||||
float maxb = s+threshold;
|
||||
float minb = s-threshold;
|
||||
|
||||
int y_lim = int(ove_resolution.y);
|
||||
|
||||
for (int i = 0; i < y_lim; i++) {
|
||||
vec3 x = texture2D(ove_maintex, vec2(ove_texcoord.x, float(i)/float(ove_resolution.y))).rgb;
|
||||
col += step(x, vec3(maxb))*step(vec3(minb), x) / (ove_resolution.y * 0.125);
|
||||
|
||||
float l = dot(x, x);
|
||||
col += step(l, maxb*maxb)*step(minb*minb, l) / (ove_resolution.y * 0.125);
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(col, 1.0);
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
<file>dropshadow.xml</file>
|
||||
<file>diptoblack.frag</file>
|
||||
<file>diptoblack.xml</file>
|
||||
<file>rgbwaveform.frag</file>
|
||||
<file>solid.frag</file>
|
||||
<file>solid.xml</file>
|
||||
<file>stroke.frag</file>
|
||||
|
||||
@@ -34,6 +34,7 @@ add_subdirectory(playbackcontrols)
|
||||
add_subdirectory(projectexplorer)
|
||||
add_subdirectory(projecttoolbar)
|
||||
add_subdirectory(resizablescrollbar)
|
||||
add_subdirectory(scope)
|
||||
add_subdirectory(slider)
|
||||
add_subdirectory(taskview)
|
||||
add_subdirectory(timebased)
|
||||
|
||||
@@ -79,7 +79,7 @@ NodeParamView::NodeParamView(QWidget *parent) :
|
||||
// Connect ruler and keyframe view together
|
||||
connect(ruler(), &TimeRuler::TimeChanged, keyframe_view_, &KeyframeView::SetTime);
|
||||
connect(keyframe_view_, &KeyframeView::TimeChanged, ruler(), &TimeRuler::SetTime);
|
||||
connect(keyframe_view_, &KeyframeView::TimeChanged, this, &NodeParamView::SetTime);
|
||||
connect(keyframe_view_, &KeyframeView::TimeChanged, this, &NodeParamView::SetTimestamp);
|
||||
|
||||
// Connect keyframe view scaling to this
|
||||
connect(keyframe_view_, &KeyframeView::ScaleChanged, this, &NodeParamView::SetScale);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(histogram)
|
||||
add_subdirectory(waveform)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/scope/histogram/histogram.h
|
||||
widget/scope/histogram/histogram.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,42 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "histogram.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
HistogramScope::HistogramScope(QWidget* parent) :
|
||||
QOpenGLWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void HistogramScope::SetBuffer(Frame* frame)
|
||||
{
|
||||
buffer_ = frame;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void HistogramScope::paintGL()
|
||||
{
|
||||
//QPainter p(this);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -0,0 +1,49 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef HISTOGRAMSCOPE_H
|
||||
#define HISTOGRAMSCOPE_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
|
||||
#include "codec/frame.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class HistogramScope : public QOpenGLWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HistogramScope(QWidget* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void SetBuffer(Frame* frame);
|
||||
|
||||
protected:
|
||||
virtual void paintGL() override;
|
||||
|
||||
private:
|
||||
Frame* buffer_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // HISTOGRAMSCOPE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/scope/waveform/waveform.h
|
||||
widget/scope/waveform/waveform.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "waveform.h"
|
||||
|
||||
#include "node/node.h"
|
||||
#include "render/backend/opengl/openglrenderfunctions.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
WaveformScope::WaveformScope(QWidget* parent) :
|
||||
QOpenGLWidget(parent),
|
||||
texture_(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WaveformScope::SetTexture(OpenGLTexture *texture)
|
||||
{
|
||||
texture_ = texture;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void WaveformScope::initializeGL()
|
||||
{
|
||||
pipeline_ = OpenGLShader::Create();
|
||||
pipeline_->create();
|
||||
pipeline_->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex());
|
||||
pipeline_->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(":/shaders/rgbwaveform.frag"));
|
||||
pipeline_->link();
|
||||
}
|
||||
|
||||
void WaveformScope::paintGL()
|
||||
{
|
||||
context()->functions()->glClearColor(0, 0, 0, 0);
|
||||
context()->functions()->glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (!pipeline_ || !texture_) {
|
||||
return;
|
||||
}
|
||||
|
||||
pipeline_->bind();
|
||||
pipeline_->setUniformValue("ove_resolution", texture_->width(), texture_->height());
|
||||
|
||||
// The general size of a pixel
|
||||
pipeline_->setUniformValue("threshold", 2.0f / static_cast<float>(height()));
|
||||
|
||||
pipeline_->release();
|
||||
|
||||
texture_->Bind();
|
||||
|
||||
OpenGLRenderFunctions::Blit(pipeline_);
|
||||
|
||||
texture_->Release();
|
||||
}
|
||||
|
||||
void WaveformScope::CleanUp()
|
||||
{
|
||||
pipeline_ = nullptr;
|
||||
texture_ = nullptr;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -0,0 +1,59 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef WAVEFORMSCOPE_H
|
||||
#define WAVEFORMSCOPE_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "render/backend/opengl/openglcolorprocessor.h"
|
||||
#include "render/backend/opengl/openglshader.h"
|
||||
#include "render/backend/opengl/opengltexture.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class WaveformScope : public QOpenGLWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaveformScope(QWidget* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void SetTexture(OpenGLTexture* texture);
|
||||
|
||||
protected:
|
||||
virtual void initializeGL() override;
|
||||
|
||||
virtual void paintGL() override;
|
||||
|
||||
private:
|
||||
OpenGLShaderPtr pipeline_;
|
||||
|
||||
OpenGLTexture* texture_;
|
||||
|
||||
private slots:
|
||||
void CleanUp();
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // WAVEFORMSCOPE_H
|
||||
@@ -204,7 +204,7 @@ void TimeBasedWidget::ConnectTimelineView(TimelineViewBase *base)
|
||||
timeline_views_.append(base);
|
||||
}
|
||||
|
||||
void TimeBasedWidget::SetTime(int64_t timestamp)
|
||||
void TimeBasedWidget::SetTimestamp(int64_t timestamp)
|
||||
{
|
||||
ruler_->SetTime(timestamp);
|
||||
|
||||
@@ -325,7 +325,7 @@ void TimeBasedWidget::GoToEnd()
|
||||
|
||||
void TimeBasedWidget::SetTimeAndSignal(const int64_t &t)
|
||||
{
|
||||
SetTime(t);
|
||||
SetTimestamp(t);
|
||||
emit TimeChanged(t);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,8 +53,7 @@ public:
|
||||
void SetScaleAndCenterOnPlayhead(const double& scale);
|
||||
|
||||
public slots:
|
||||
// FIXME: Rename this to SetTimestamp to reduce confusion
|
||||
void SetTime(int64_t timestamp);
|
||||
void SetTimestamp(int64_t timestamp);
|
||||
|
||||
void SetTimebase(const rational& timebase);
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "project/project.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -64,6 +65,9 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
ViewerGLWidget* main_widget = new ViewerGLWidget();
|
||||
connect(main_widget, &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
|
||||
connect(main_widget, &ViewerGLWidget::CursorColor, this, &ViewerWidget::CursorColor);
|
||||
connect(main_widget, &ViewerGLWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer);
|
||||
connect(main_widget, &ViewerGLWidget::LoadedTexture, this, &ViewerWidget::LoadedTexture);
|
||||
connect(main_widget, &ViewerGLWidget::DrewManagedTexture, this, &ViewerWidget::DrewManagedTexture);
|
||||
connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerGLWidget::SetMatrix);
|
||||
sizer_->SetWidget(main_widget);
|
||||
gl_widgets_.append(main_widget);
|
||||
@@ -259,7 +263,7 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node, ColorManager* color_man
|
||||
TimeBasedWidget::ConnectViewerNode(node);
|
||||
|
||||
// Set texture to new texture (or null if no viewer node is available)
|
||||
UpdateTextureFromNode(GetTime());
|
||||
ForceUpdate();
|
||||
}
|
||||
|
||||
void ViewerWidget::SetColorMenuEnabled(bool enabled)
|
||||
@@ -313,6 +317,12 @@ void ViewerWidget::SetFullScreen(QScreen *screen)
|
||||
gl_widgets_.append(vw->gl_widget());
|
||||
}
|
||||
|
||||
void ViewerWidget::ForceUpdate()
|
||||
{
|
||||
// Hack that forces the viewer to update
|
||||
UpdateTextureFromNode(GetTime());
|
||||
}
|
||||
|
||||
VideoRenderBackend *ViewerWidget::video_renderer() const
|
||||
{
|
||||
return video_renderer_;
|
||||
@@ -458,7 +468,6 @@ void ViewerWidget::ContextMenuSetCustomSafeMargins()
|
||||
if (ratio_components.size() == 2) {
|
||||
bool numer_ok, denom_ok;
|
||||
|
||||
// FIXME: Won't accept decimals like 2.39:1
|
||||
double num = ratio_components.at(0).toDouble(&numer_ok);
|
||||
double den = ratio_components.at(1).toDouble(&denom_ok);
|
||||
|
||||
@@ -486,6 +495,11 @@ void ViewerWidget::WindowAboutToClose()
|
||||
gl_widgets_.removeAll(vw->gl_widget());
|
||||
}
|
||||
|
||||
void ViewerWidget::ContextMenuScopeTriggered(QAction *action)
|
||||
{
|
||||
emit RequestScopePanel(static_cast<ScopePanel::Type>(action->data().toInt()));
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateRendererParameters()
|
||||
{
|
||||
if (!GetConnectedNode()) {
|
||||
@@ -626,6 +640,21 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos)
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
{
|
||||
// Scopes
|
||||
Menu* scopes_menu = new Menu(tr("Scopes"), &menu);
|
||||
menu.addMenu(scopes_menu);
|
||||
|
||||
for (int i=0;i<ScopePanel::kTypeCount;i++) {
|
||||
QAction* scope_action = scopes_menu->addAction(ScopePanel::TypeToName(static_cast<ScopePanel::Type>(i)));
|
||||
scope_action->setData(i);
|
||||
}
|
||||
|
||||
connect(scopes_menu, &Menu::triggered, this, &ViewerWidget::ContextMenuScopeTriggered);
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
{
|
||||
// Safe Margins
|
||||
Menu* safe_margin_menu = new Menu(tr("Safe Margins"), &menu);
|
||||
@@ -768,6 +797,11 @@ void ViewerWidget::SetSignalCursorColorEnabled(bool e)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::SetEmitDrewManagedTextureEnabled(bool e)
|
||||
{
|
||||
main_gl_widget()->SetEmitDrewManagedTextureEnabled(e);
|
||||
}
|
||||
|
||||
void ViewerWidget::TimebaseChangedEvent(const rational &timebase)
|
||||
{
|
||||
TimeBasedWidget::TimebaseChangedEvent(timebase);
|
||||
@@ -852,7 +886,7 @@ void ViewerWidget::RendererCachedTime(const rational &time, qint64 job_time)
|
||||
if (GetTime() == time && job_time > frame_cache_job_time_) {
|
||||
frame_cache_job_time_ = job_time;
|
||||
|
||||
UpdateTextureFromNode(GetTime());
|
||||
ForceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "audiowaveformview.h"
|
||||
#include "common/rational.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "panel/scope/scope.h"
|
||||
#include "render/backend/opengl/openglbackend.h"
|
||||
#include "render/backend/opengl/opengltexture.h"
|
||||
#include "render/backend/audio/audiobackend.h"
|
||||
@@ -80,6 +81,8 @@ public:
|
||||
*/
|
||||
void SetFullScreen(QScreen* screen = nullptr);
|
||||
|
||||
void ForceUpdate();
|
||||
|
||||
VideoRenderBackend* video_renderer() const;
|
||||
|
||||
public slots:
|
||||
@@ -123,12 +126,39 @@ public slots:
|
||||
*/
|
||||
void SetSignalCursorColorEnabled(bool e);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::SetEmitDrewManagedTextureEnabled()
|
||||
*/
|
||||
void SetEmitDrewManagedTextureEnabled(bool e);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::CursorColor()
|
||||
*/
|
||||
void CursorColor(const Color& reference, const Color& display);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::LoadedBuffer()
|
||||
*/
|
||||
void LoadedBuffer(Frame* load_buffer);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::LoadedTexture()
|
||||
*/
|
||||
void LoadedTexture(OpenGLTexture* texture);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::DrewManagedTexture()
|
||||
*/
|
||||
void DrewManagedTexture(OpenGLTexture* texture);
|
||||
|
||||
/**
|
||||
* @brief Request a scope panel
|
||||
*
|
||||
* As a widget, we don't handle panels, but a parent panel may pick this signal up.
|
||||
*/
|
||||
void RequestScopePanel(ScopePanel::Type type);
|
||||
|
||||
protected:
|
||||
virtual void TimebaseChangedEvent(const rational &) override;
|
||||
virtual void TimeChangedEvent(const int64_t &) override;
|
||||
@@ -244,6 +274,8 @@ private slots:
|
||||
|
||||
void WindowAboutToClose();
|
||||
|
||||
void ContextMenuScopeTriggered(QAction* action);
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -42,9 +42,11 @@ bool ViewerGLWidget::nouveau_check_done_ = false;
|
||||
|
||||
ViewerGLWidget::ViewerGLWidget(QWidget *parent) :
|
||||
QOpenGLWidget(parent),
|
||||
managed_copy_pipeline_(nullptr),
|
||||
color_manager_(nullptr),
|
||||
has_image_(false),
|
||||
signal_cursor_color_(false)
|
||||
signal_cursor_color_(false),
|
||||
enable_display_referred_signal_(false)
|
||||
{
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
}
|
||||
@@ -115,9 +117,11 @@ void ViewerGLWidget::SetImage(const QString &fn)
|
||||
input->read_image(input->spec().format, load_buffer_.data());
|
||||
input->close();
|
||||
|
||||
emit LoadedBuffer(&load_buffer_);
|
||||
|
||||
texture_.Upload(load_buffer_.data());
|
||||
|
||||
emit LoadedBuffer(&load_buffer_);
|
||||
emit LoadedTexture(&texture_);
|
||||
|
||||
doneCurrent();
|
||||
|
||||
@@ -169,6 +173,18 @@ void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer)
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerGLWidget::SetEmitDrewManagedTextureEnabled(bool e)
|
||||
{
|
||||
enable_display_referred_signal_ = e;
|
||||
|
||||
if (!enable_display_referred_signal_) {
|
||||
// Destroy the texture now
|
||||
managed_texture_.Destroy();
|
||||
managed_copy_pipeline_ = nullptr;
|
||||
framebuffer_.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerGLWidget::SetOCIODisplay(const QString &display)
|
||||
{
|
||||
ocio_display_ = display;
|
||||
@@ -310,6 +326,34 @@ void ViewerGLWidget::paintGL()
|
||||
|
||||
// We only draw if we have a pipeline
|
||||
if (has_image_ && color_service_ && texture_.IsCreated()) {
|
||||
|
||||
// If we're distributing our display-referred final buffer, we'll have to make a copy of it
|
||||
if (enable_display_referred_signal_) {
|
||||
|
||||
if (!managed_texture_.IsCreated()
|
||||
|| managed_texture_.width() != texture_.width()
|
||||
|| managed_texture_.height() != texture_.height()
|
||||
|| managed_texture_.format() != texture_.format()) {
|
||||
managed_texture_.Destroy();
|
||||
|
||||
managed_texture_.Create(context(), texture_.width(), texture_.height(), texture_.format());
|
||||
}
|
||||
|
||||
if (!managed_copy_pipeline_) {
|
||||
managed_copy_pipeline_ = OpenGLShader::CreateDefault();
|
||||
}
|
||||
|
||||
if (!framebuffer_.IsCreated()) {
|
||||
framebuffer_.Create(context());
|
||||
}
|
||||
|
||||
framebuffer_.Attach(&managed_texture_);
|
||||
framebuffer_.Bind();
|
||||
|
||||
context()->functions()->glViewport(0, 0, managed_texture_.width(), managed_texture_.height());
|
||||
|
||||
}
|
||||
|
||||
// Bind retrieved texture
|
||||
f->glBindTexture(GL_TEXTURE_2D, texture_.texture());
|
||||
|
||||
@@ -318,6 +362,25 @@ void ViewerGLWidget::paintGL()
|
||||
|
||||
// Release retrieved texture
|
||||
f->glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (enable_display_referred_signal_) {
|
||||
|
||||
framebuffer_.Release();
|
||||
framebuffer_.Detach();
|
||||
|
||||
emit DrewManagedTexture(&managed_texture_);
|
||||
|
||||
// Bind retrieved texture
|
||||
managed_texture_.Bind();
|
||||
|
||||
context()->functions()->glViewport(0, 0, width(), height());
|
||||
|
||||
OpenGLRenderFunctions::Blit(managed_copy_pipeline_);
|
||||
|
||||
// Bind retrieved texture
|
||||
managed_texture_.Release();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Draw action/title safe areas
|
||||
@@ -433,7 +496,10 @@ void ViewerGLWidget::ContextCleanup()
|
||||
makeCurrent();
|
||||
|
||||
color_service_ = nullptr;
|
||||
managed_copy_pipeline_ = nullptr;
|
||||
texture_.Destroy();
|
||||
managed_texture_.Destroy();
|
||||
framebuffer_.Destroy();
|
||||
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
@@ -148,6 +148,15 @@ public slots:
|
||||
*/
|
||||
void SetImageFromLoadBuffer(Frame* in_buffer);
|
||||
|
||||
/**
|
||||
* @brief Enables or disables DrewManagedTexture()
|
||||
*
|
||||
* To emit a display referred texture, it needs to be copied after the color transform is complete. This naturally
|
||||
* adds extra GPU cycles that are wasted if there's nothing receiving the signal. Therefore, the signal is disabled
|
||||
* by default.
|
||||
*/
|
||||
void SetEmitDrewManagedTextureEnabled(bool e);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when the user starts dragging from the viewer
|
||||
@@ -160,10 +169,26 @@ signals:
|
||||
void CursorColor(const Color& reference, const Color& display);
|
||||
|
||||
/**
|
||||
* @brief Connect this to the SetImageFromLoadBuffer() slot of another ViewerGLWidget to show the same thing
|
||||
* @brief Signal emitted when a buffer is loaded from file into memory
|
||||
*
|
||||
* This buffer will be the direct output of the renderer in reference space in CPU memory.
|
||||
*
|
||||
* Connect this to the SetImageFromLoadBuffer() slot of another ViewerGLWidget to show the same thing
|
||||
*/
|
||||
void LoadedBuffer(Frame* load_buffer);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when a buffer is loaded into a texture
|
||||
*
|
||||
* This texture will be the direct output of the renderer in reference space in GPU VRAM.
|
||||
*/
|
||||
void LoadedTexture(OpenGLTexture* texture);
|
||||
|
||||
/**
|
||||
* @brief Emitted when the a texture has been transformed to display
|
||||
*/
|
||||
void DrewManagedTexture(OpenGLTexture* texture);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Override the mouse press event simply to emit the DragStarted() signal
|
||||
@@ -220,6 +245,23 @@ private:
|
||||
*/
|
||||
OpenGLTexture texture_;
|
||||
|
||||
/**
|
||||
* @brief Internal framebuffer used to draw to managed_texture_
|
||||
*/
|
||||
OpenGLFramebuffer framebuffer_;
|
||||
|
||||
/**
|
||||
* @brief Internal referenceto the OpenGL texture that's been managed
|
||||
*
|
||||
* Kept so that scopes can use the display-referred buffer without having to transform again.
|
||||
*/
|
||||
OpenGLTexture managed_texture_;
|
||||
|
||||
/**
|
||||
* @brief Pipeline used to draw to managed_texture_
|
||||
*/
|
||||
OpenGLShaderPtr managed_copy_pipeline_;
|
||||
|
||||
/**
|
||||
* @brief Connected color manager
|
||||
*/
|
||||
@@ -250,6 +292,8 @@ private:
|
||||
|
||||
ViewerSafeMarginInfo safe_margin_;
|
||||
|
||||
bool enable_display_referred_signal_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot to connect just before the OpenGL context is destroyed to clean up resources
|
||||
|
||||
@@ -244,6 +244,21 @@ void MainWindow::FolderOpen(Project* p, Item *i, bool floating)
|
||||
folder_panels_.append(panel);
|
||||
}
|
||||
|
||||
ScopePanel *MainWindow::AppendScopePanel()
|
||||
{
|
||||
ScopePanel* panel = PanelManager::instance()->CreatePanel<ScopePanel>(this);
|
||||
|
||||
SetUniquePanelID(panel, scope_panels_);
|
||||
|
||||
panel->setFloating(true);
|
||||
panel->show();
|
||||
|
||||
panel->SetSignalInsteadOfClose(true);
|
||||
connect(panel, &ScopePanel::CloseRequested, this, &MainWindow::ScopeCloseRequested);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
void MainWindow::SetFullscreen(bool fullscreen)
|
||||
{
|
||||
if (fullscreen) {
|
||||
@@ -408,6 +423,14 @@ void MainWindow::FolderCloseRequested()
|
||||
panel->deleteLater();
|
||||
}
|
||||
|
||||
void MainWindow::ScopeCloseRequested()
|
||||
{
|
||||
ScopePanel* panel = static_cast<ScopePanel*>(sender());
|
||||
|
||||
scope_panels_.removeOne(panel);
|
||||
panel->deleteLater();
|
||||
}
|
||||
|
||||
void MainWindow::LoadLayoutInternal(QXmlStreamReader *reader, XMLNodeData *xml_data)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "panel/node/node.h"
|
||||
#include "panel/param/param.h"
|
||||
#include "panel/project/project.h"
|
||||
#include "panel/scope/scope.h"
|
||||
#include "panel/taskmanager/taskmanager.h"
|
||||
#include "panel/timeline/timeline.h"
|
||||
#include "panel/tool/tool.h"
|
||||
@@ -63,14 +64,16 @@ public:
|
||||
|
||||
bool IsSequenceOpen(Sequence* sequence) const;
|
||||
|
||||
void FolderOpen(Project* p, Item* i, bool floating);
|
||||
|
||||
ScopePanel* AppendScopePanel();
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
void SetTaskbarButtonState(TBPFLAG flags);
|
||||
|
||||
void SetTaskbarButtonProgress(int value, int max);
|
||||
#endif
|
||||
|
||||
void FolderOpen(Project* p, Item* i, bool floating);
|
||||
|
||||
public slots:
|
||||
void ProjectOpen(Project *p);
|
||||
|
||||
@@ -121,6 +124,7 @@ private:
|
||||
TaskManagerPanel* task_man_panel_;
|
||||
CurvePanel* curve_panel_;
|
||||
PixelSamplerPanel* pixel_sampler_panel_;
|
||||
QList<ScopePanel*> scope_panels_;
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
unsigned int taskbar_btn_id_;
|
||||
@@ -139,6 +143,8 @@ private slots:
|
||||
|
||||
void FolderCloseRequested();
|
||||
|
||||
void ScopeCloseRequested();
|
||||
|
||||
void LoadLayoutInternal(QXmlStreamReader* reader, XMLNodeData *xml_data);
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user