diff --git a/app/widget/scope/CMakeLists.txt b/app/widget/scope/CMakeLists.txt index a14b351dd..91c4f0b44 100644 --- a/app/widget/scope/CMakeLists.txt +++ b/app/widget/scope/CMakeLists.txt @@ -15,6 +15,7 @@ # along with this program. If not, see . add_subdirectory(histogram) +add_subdirectory(scopebase) add_subdirectory(waveform) set(OLIVE_SOURCES diff --git a/app/widget/scope/histogram/histogram.cpp b/app/widget/scope/histogram/histogram.cpp index b9028f85a..9665e32de 100644 --- a/app/widget/scope/histogram/histogram.cpp +++ b/app/widget/scope/histogram/histogram.cpp @@ -29,189 +29,8 @@ OLIVE_NAMESPACE_ENTER HistogramScope::HistogramScope(QWidget* parent) : - ManagedDisplayWidget(parent), - buffer_(nullptr) + ScopeBase(parent) { - EnableDefaultContextMenu(); - - connect(&worker_, &HistogramScopeWorker::Finished, this, &HistogramScope::FinishedProcessing, Qt::QueuedConnection); - worker_.start(QThread::IdlePriority); -} - -HistogramScope::~HistogramScope() -{ - worker_.Cancel(); - worker_.quit(); - worker_.wait(); -} - -void HistogramScope::SetBuffer(Frame* frame) -{ - buffer_ = frame; - - if (isVisible()) { - StartUpdate(); - } -} - -void HistogramScope::FinishedProcessing(QVector red, QVector green, QVector blue) -{ - red_val_ = red; - green_val_ = green; - blue_val_ = blue; - - update(); -} - -void HistogramScope::paintGL() -{ - QVector red_lines(red_val_.size()); - QVector green_lines(green_val_.size()); - QVector blue_lines(blue_val_.size()); - - for (int i=0;i data(w * kRGBChannels, 0); - - int max_w = w-1; - - for (int x=0;xConvertColor(c); - } - - data[qFloor(clamp(c.red(), 0.0f, 1.0f) * max_w)]++; - data[qFloor(clamp(c.green(), 0.0f, 1.0f) * max_w) + w]++; - data[qFloor(clamp(c.blue(), 0.0f, 1.0f) * max_w) + w * 2]++; - } - } - - int max_val = 0; - - foreach (const int& i, data) { - if (i > max_val) { - max_val = i; - } - } - - if (!max_val) { - // Prevent divide by zero - return; - } - - QVector red_lines(w); - QVector green_lines(w); - QVector blue_lines(w); - - for (int i=0;i(data.at(i)) / static_cast(max_val)); - green_lines.replace(i, static_cast(data.at(i + w)) / static_cast(max_val)); - blue_lines.replace(i, static_cast(data.at(i + w * 2)) / static_cast(max_val)); - } - - emit Finished(red_lines, green_lines, blue_lines); - } -} - -void HistogramScopeWorker::QueueNext(const Frame &f, ColorProcessorPtr processor, int width) -{ - next_lock_.lock(); - - next_ = f; - next_width_ = width; - next_processor_ = processor; - - next_wait_.wakeOne(); - - next_lock_.unlock(); -} - -void HistogramScopeWorker::Cancel() -{ - cancelled_ = true; - next_lock_.lock(); - next_wait_.wakeOne(); - next_lock_.unlock(); } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/histogram/histogram.h b/app/widget/scope/histogram/histogram.h index 3efad3919..625074a89 100644 --- a/app/widget/scope/histogram/histogram.h +++ b/app/widget/scope/histogram/histogram.h @@ -21,80 +21,20 @@ #ifndef HISTOGRAMSCOPE_H #define HISTOGRAMSCOPE_H -#include -#include -#include - -#include "codec/frame.h" -#include "render/colorprocessor.h" -#include "widget/manageddisplay/manageddisplay.h" +#include "widget/scope/scopebase/scopebase.h" OLIVE_NAMESPACE_ENTER -class HistogramScopeWorker : public QThread -{ - Q_OBJECT -public: - HistogramScopeWorker(); - - // Thread-safe - void QueueNext(const Frame& f, ColorProcessorPtr processor, int width); - - // Thread-safe - void Cancel(); - -protected: - virtual void run() override; - -signals: - void Finished(QVector red, QVector green, QVector blue); - -private: - QAtomicInt cancelled_; - - QMutex next_lock_; - QWaitCondition next_wait_; - Frame next_; - int next_width_; - ColorProcessorPtr next_processor_; - -}; - -class HistogramScope : public ManagedDisplayWidget +class HistogramScope : public ScopeBase { Q_OBJECT public: HistogramScope(QWidget* parent = nullptr); - virtual ~HistogramScope() override; - -public slots: - void SetBuffer(Frame* frame); - protected: - virtual void paintGL() override; + //virtual OpenGLShaderPtr CreateShader() override; - virtual void resizeEvent(QResizeEvent* e) override; - - virtual void ColorProcessorChangedEvent() override; - - virtual void showEvent(QShowEvent* e) override; - -private: - void StartUpdate(); - - Frame* buffer_; - - QVector red_val_; - - QVector green_val_; - - QVector blue_val_; - - HistogramScopeWorker worker_; - -private slots: - void FinishedProcessing(QVector red, QVector green, QVector blue); + //virtual void DrawScope() override; }; diff --git a/app/widget/scope/scopebase/CMakeLists.txt b/app/widget/scope/scopebase/CMakeLists.txt new file mode 100644 index 000000000..ca6d924c3 --- /dev/null +++ b/app/widget/scope/scopebase/CMakeLists.txt @@ -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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/scope/scopebase/scopebase.h + widget/scope/scopebase/scopebase.cpp + PARENT_SCOPE +) diff --git a/app/widget/scope/scopebase/scopebase.cpp b/app/widget/scope/scopebase/scopebase.cpp new file mode 100644 index 000000000..c58b9e8bd --- /dev/null +++ b/app/widget/scope/scopebase/scopebase.cpp @@ -0,0 +1,163 @@ +/*** + + 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 . + +***/ + +#include "scopebase.h" + +#include "render/backend/opengl/openglrenderfunctions.h" + +OLIVE_NAMESPACE_ENTER + +ScopeBase::ScopeBase(QWidget* parent) : + ManagedDisplayWidget(parent), + buffer_(nullptr) +{ + EnableDefaultContextMenu(); +} + +ScopeBase::~ScopeBase() +{ + CleanUp(); + + if (context()) { + disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ScopeBase::CleanUp); + } +} + +void ScopeBase::SetBuffer(Frame *frame) +{ + buffer_ = frame; + + UploadTextureFromBuffer(); +} + +void ScopeBase::showEvent(QShowEvent* e) +{ + ManagedDisplayWidget::showEvent(e); + + UploadTextureFromBuffer(); +} + +OpenGLShaderPtr ScopeBase::CreateShader() +{ + return OpenGLShader::CreateDefault(); +} + +void ScopeBase::DrawScope() +{ + managed_tex().Bind(); + + OpenGLRenderFunctions::Blit(pipeline()); + + managed_tex().Release(); +} + +OpenGLShaderPtr ScopeBase::pipeline() +{ + return pipeline_; +} + +OpenGLTexture &ScopeBase::managed_tex() +{ + return managed_tex_; +} + +void ScopeBase::UploadTextureFromBuffer() +{ + if (!isVisible()) { + return; + } + + if (buffer_) { + makeCurrent(); + + if (!texture_.IsCreated() + || texture_.width() != buffer_->width() + || texture_.height() != buffer_->height() + || texture_.format() != buffer_->format()) { + texture_.Destroy(); + managed_tex_.Destroy(); + + texture_.Create(context(), buffer_); + managed_tex_.Create(context(), buffer_->video_params()); + } else { + texture_.Upload(buffer_); + } + + doneCurrent(); + } + + update(); +} + +void ScopeBase::CleanUp() +{ + makeCurrent(); + + pipeline_ = nullptr; + texture_.Destroy(); + managed_tex_.Destroy(); + framebuffer_.Destroy(); + + doneCurrent(); +} + +void ScopeBase::initializeGL() +{ + ManagedDisplayWidget::initializeGL(); + + pipeline_ = CreateShader(); + + framebuffer_.Create(context()); + + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ScopeBase::CleanUp, Qt::DirectConnection); + + UploadTextureFromBuffer(); +} + +void ScopeBase::paintGL() +{ + QOpenGLFunctions* f = context()->functions(); + + f->glClearColor(0, 0, 0, 0); + f->glClear(GL_COLOR_BUFFER_BIT); + + if (buffer_ && pipeline() && texture_.IsCreated()) { + // Convert reference frame to display space + framebuffer_.Attach(&managed_tex_); + framebuffer_.Bind(); + + texture_.Bind(); + + f->glViewport(0, 0, texture_.width(), texture_.height()); + + color_service()->ProcessOpenGL(); + + texture_.Release(); + + framebuffer_.Release(); + framebuffer_.Detach(); + + f->glViewport(0, 0, width(), height()); + + DrawScope(); + } +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/scopebase/scopebase.h b/app/widget/scope/scopebase/scopebase.h new file mode 100644 index 000000000..3098af212 --- /dev/null +++ b/app/widget/scope/scopebase/scopebase.h @@ -0,0 +1,78 @@ +/*** + + 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 . + +***/ + +#ifndef SCOPEBASE_H +#define SCOPEBASE_H + +#include "codec/frame.h" +#include "render/backend/opengl/openglcolorprocessor.h" +#include "render/backend/opengl/openglframebuffer.h" +#include "render/backend/opengl/openglshader.h" +#include "render/backend/opengl/opengltexture.h" +#include "widget/manageddisplay/manageddisplay.h" + +OLIVE_NAMESPACE_ENTER + +class ScopeBase : public ManagedDisplayWidget +{ +public: + ScopeBase(QWidget* parent = nullptr); + + virtual ~ScopeBase() override; + +public slots: + void SetBuffer(Frame* frame); + +protected: + virtual void initializeGL() override; + + virtual void paintGL() override; + + virtual void showEvent(QShowEvent* e) override; + + virtual OpenGLShaderPtr CreateShader(); + + virtual void DrawScope(); + + OpenGLShaderPtr pipeline(); + + OpenGLTexture& managed_tex(); + +private: + void UploadTextureFromBuffer(); + + OpenGLShaderPtr pipeline_; + + OpenGLTexture texture_; + + OpenGLTexture managed_tex_; + + OpenGLFramebuffer framebuffer_; + + Frame* buffer_; + +private slots: + void CleanUp(); + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // SCOPEBASE_H diff --git a/app/widget/scope/waveform/waveform.cpp b/app/widget/scope/waveform/waveform.cpp index 5f9225b3f..6c3c43fc7 100644 --- a/app/widget/scope/waveform/waveform.cpp +++ b/app/widget/scope/waveform/waveform.cpp @@ -32,59 +32,24 @@ OLIVE_NAMESPACE_ENTER WaveformScope::WaveformScope(QWidget* parent) : - ManagedDisplayWidget(parent), - buffer_(nullptr) + ScopeBase(parent) { - EnableDefaultContextMenu(); } -WaveformScope::~WaveformScope() +OpenGLShaderPtr WaveformScope::CreateShader() { - CleanUp(); + OpenGLShaderPtr pipeline = OpenGLShader::Create(); - if (context()) { - disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &WaveformScope::CleanUp); - } + pipeline->create(); + pipeline->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex()); + pipeline->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(":/shaders/rgbwaveform.frag")); + pipeline->link(); + + return pipeline; } -void WaveformScope::SetBuffer(Frame *frame) +void WaveformScope::DrawScope() { - buffer_ = frame; - - UploadTextureFromBuffer(); -} - -void WaveformScope::showEvent(QShowEvent* e) -{ - ManagedDisplayWidget::showEvent(e); - - UploadTextureFromBuffer(); -} - -void WaveformScope::initializeGL() -{ - ManagedDisplayWidget::initializeGL(); - - pipeline_ = OpenGLShader::Create(); - pipeline_->create(); - pipeline_->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex()); - pipeline_->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(":/shaders/rgbwaveform.frag")); - pipeline_->link(); - - framebuffer_.Create(context()); - - connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &WaveformScope::CleanUp, Qt::DirectConnection); - - UploadTextureFromBuffer(); -} - -void WaveformScope::paintGL() -{ - QOpenGLFunctions* f = context()->functions(); - - f->glClearColor(0, 0, 0, 0); - f->glClear(GL_COLOR_BUFFER_BIT); - float waveform_scale = 0.80f; float waveform_dim_x = width() * waveform_scale; float waveform_dim_y = height() * waveform_scale; @@ -93,59 +58,40 @@ void WaveformScope::paintGL() float waveform_end_dim_x = width() - waveform_start_dim_x; float waveform_end_dim_y = height() - waveform_start_dim_y; - if (buffer_ && pipeline_ && texture_.IsCreated()) { - // Convert reference frame to display space - framebuffer_.Attach(&managed_tex_); - framebuffer_.Bind(); + // Draw waveform through shader + pipeline()->bind(); + pipeline()->setUniformValue("ove_resolution", managed_tex().width(), managed_tex().height()); + pipeline()->setUniformValue("ove_viewport", width(), height()); + GLfloat luma[3] = {0.0, 0.0, 0.0}; + color_manager()->GetDefaultLumaCoefs(luma); + pipeline()->setUniformValue("luma_coeffs", luma[0], luma[1], luma[2]); - texture_.Bind(); + // Scale of the waveform relative to the viewport surface. + pipeline()->setUniformValue("waveform_scale", waveform_scale); + pipeline()->setUniformValue( + "waveform_dims", waveform_dim_x, waveform_dim_y); - f->glViewport(0, 0, texture_.width(), texture_.height()); + pipeline()->setUniformValue( + "waveform_region", + waveform_start_dim_x, waveform_start_dim_y, + waveform_end_dim_x, waveform_end_dim_y); - color_service()->ProcessOpenGL(); + float waveform_start_uv_x = waveform_start_dim_x / width(); + float waveform_start_uv_y = waveform_start_dim_y / height(); + float waveform_end_uv_x = waveform_end_dim_x / width(); + float waveform_end_uv_y = waveform_end_dim_y / height(); + pipeline()->setUniformValue( + "waveform_uv", + waveform_start_uv_x, waveform_start_uv_y, + waveform_end_uv_x, waveform_end_uv_y); - texture_.Release(); + pipeline()->release(); - framebuffer_.Release(); - framebuffer_.Detach(); + managed_tex().Bind(); - // Draw waveform through shader - pipeline_->bind(); - pipeline_->setUniformValue("ove_resolution", texture_.width(), texture_.height()); - pipeline_->setUniformValue("ove_viewport", width(), height()); - GLfloat luma[3] = {0.0, 0.0, 0.0}; - color_manager()->GetDefaultLumaCoefs(luma); - pipeline_->setUniformValue("luma_coeffs", luma[0], luma[1], luma[2]); + OpenGLRenderFunctions::Blit(pipeline()); - // Scale of the waveform relative to the viewport surface. - pipeline_->setUniformValue("waveform_scale", waveform_scale); - pipeline_->setUniformValue( - "waveform_dims", waveform_dim_x, waveform_dim_y); - - pipeline_->setUniformValue( - "waveform_region", - waveform_start_dim_x, waveform_start_dim_y, - waveform_end_dim_x, waveform_end_dim_y); - - float waveform_start_uv_x = waveform_start_dim_x / width(); - float waveform_start_uv_y = waveform_start_dim_y / height(); - float waveform_end_uv_x = waveform_end_dim_x / width(); - float waveform_end_uv_y = waveform_end_dim_y / height(); - pipeline_->setUniformValue( - "waveform_uv", - waveform_start_uv_x, waveform_start_uv_y, - waveform_end_uv_x, waveform_end_uv_y); - - pipeline_->release(); - - f->glViewport(0, 0, width(), height()); - - managed_tex_.Bind(); - - OpenGLRenderFunctions::Blit(pipeline_); - - managed_tex_.Release(); - } + managed_tex().Release(); // Draw line overlays QPainter p(this); @@ -179,44 +125,4 @@ void WaveformScope::paintGL() p.drawLines(ire_lines); } -void WaveformScope::UploadTextureFromBuffer() -{ - if (!isVisible()) { - return; - } - - if (buffer_) { - makeCurrent(); - - if (!texture_.IsCreated() - || texture_.width() != buffer_->width() - || texture_.height() != buffer_->height() - || texture_.format() != buffer_->format()) { - texture_.Destroy(); - managed_tex_.Destroy(); - - texture_.Create(context(), buffer_); - managed_tex_.Create(context(), buffer_->video_params()); - } else { - texture_.Upload(buffer_); - } - - doneCurrent(); - } - - update(); -} - -void WaveformScope::CleanUp() -{ - makeCurrent(); - - pipeline_ = nullptr; - texture_.Destroy(); - managed_tex_.Destroy(); - framebuffer_.Destroy(); - - doneCurrent(); -} - OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/waveform/waveform.h b/app/widget/scope/waveform/waveform.h index 4243a9d3a..04a464e52 100644 --- a/app/widget/scope/waveform/waveform.h +++ b/app/widget/scope/waveform/waveform.h @@ -21,48 +21,20 @@ #ifndef WAVEFORMSCOPE_H #define WAVEFORMSCOPE_H -#include "codec/frame.h" -#include "render/backend/opengl/openglcolorprocessor.h" -#include "render/backend/opengl/openglframebuffer.h" -#include "render/backend/opengl/openglshader.h" -#include "render/backend/opengl/opengltexture.h" -#include "widget/manageddisplay/manageddisplay.h" +#include "widget/scope/scopebase/scopebase.h" OLIVE_NAMESPACE_ENTER -class WaveformScope : public ManagedDisplayWidget +class WaveformScope : public ScopeBase { Q_OBJECT public: WaveformScope(QWidget* parent = nullptr); - virtual ~WaveformScope() override; - -public slots: - void SetBuffer(Frame* frame); - protected: - virtual void initializeGL() override; + virtual OpenGLShaderPtr CreateShader() override; - virtual void paintGL() override; - - virtual void showEvent(QShowEvent* e) override; - -private: - void UploadTextureFromBuffer(); - - OpenGLShaderPtr pipeline_; - - OpenGLTexture texture_; - - OpenGLTexture managed_tex_; - - OpenGLFramebuffer framebuffer_; - - Frame* buffer_; - -private slots: - void CleanUp(); + virtual void DrawScope() override; };