diff --git a/app/shaders/rgbhistogram.frag b/app/shaders/rgbhistogram.frag new file mode 100644 index 000000000..435949138 --- /dev/null +++ b/app/shaders/rgbhistogram.frag @@ -0,0 +1,37 @@ +#version 150 + +uniform sampler2D ove_maintex; +uniform vec2 ove_resolution; +uniform vec2 ove_viewport; + +uniform float histogram_scale; + +in vec2 ove_texcoord; + +out vec4 fragColor; + +void main(void) { + float histogram_width = ceil(histogram_scale * ove_viewport.y); + float quantisation = 1.0 / (histogram_width - 1.0); + vec3 cur_col = vec3(0.0); + 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( + ove_maintex, + vec2(ove_texcoord.y, ratio) + ).rgb; + + sum += step(vec3(ove_texcoord.x - quantisation), cur_col) * + step(cur_col, vec3(ove_texcoord.x + quantisation)) + + ( + // Account for values beyond the upper x limit. + step(1.0 - quantisation, ove_texcoord.x) * + step(vec3(1.0 - quantisation), cur_col) + ); + } + + fragColor = vec4(sum, 1.0); +} diff --git a/app/shaders/rgbhistogram.vert b/app/shaders/rgbhistogram.vert new file mode 100644 index 000000000..92536144c --- /dev/null +++ b/app/shaders/rgbhistogram.vert @@ -0,0 +1,29 @@ +#version 150 + +uniform float histogram_scale; +uniform vec2 ove_resolution; + +in vec4 a_position; +in vec2 a_texcoord; + +out vec2 ove_texcoord; + +mat4 scale_mat4(vec3 scale) { + return mat4( + scale.x, 0.0, 0.0, 0.0, + 0.0, scale.y, 0.0, 0.0, + 0.0, 0.0, scale.z, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); +} + +void main() { + // Create identity matrix + mat4 transform = mat4(1.0); + + // Scale the scope + transform *= scale_mat4(vec3(histogram_scale, histogram_scale, 1.0)); + + gl_Position = transform * a_position; + ove_texcoord = a_texcoord; +} \ No newline at end of file diff --git a/app/shaders/rgbhistogram_secondary.frag b/app/shaders/rgbhistogram_secondary.frag new file mode 100644 index 000000000..474db6b74 --- /dev/null +++ b/app/shaders/rgbhistogram_secondary.frag @@ -0,0 +1,35 @@ +#version 150 + +uniform sampler2D ove_maintex; +uniform vec2 ove_resolution; +uniform vec2 ove_viewport; + +uniform float histogram_scale; +uniform float histogram_power; + +in vec2 ove_texcoord; + +out vec4 fragColor; + +void main(void) { + vec3 col = vec3(0.0); + float histogram_height = ceil(ove_viewport.y * histogram_scale); + vec3 histogram_ratio = vec3(0.0); + vec3 sum = vec3(0.0); + float ratio = 0.0; + vec3 total_pixels = vec3(ceil(ove_viewport.x * ove_resolution.y * + histogram_scale)); + + for (int i = 0; i < histogram_height; i++) { + ratio = float(i) / float(histogram_height - 1.0); + sum += texture( + ove_maintex, + vec2(ove_texcoord.x, ratio) + ).rgb; + } + + histogram_ratio = pow(sum / total_pixels, vec3(histogram_power)); + col = step(vec3(ove_texcoord.y), histogram_ratio); + + fragColor = vec4(col, 1.0); +} diff --git a/app/widget/scope/histogram/histogram.cpp b/app/widget/scope/histogram/histogram.cpp index 9665e32de..faa546d06 100644 --- a/app/widget/scope/histogram/histogram.cpp +++ b/app/widget/scope/histogram/histogram.cpp @@ -23,8 +23,9 @@ #include #include -#include "common/clamp.h" -#include "common/functiontimer.h" +#include "common/qtutils.h" +#include "node/node.h" +#include "render/backend/opengl/openglrenderfunctions.h" OLIVE_NAMESPACE_ENTER @@ -33,4 +34,170 @@ HistogramScope::HistogramScope(QWidget* parent) : { } +HistogramScope::~HistogramScope() +{ + CleanUp(); + + if (context()) { + disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, + &HistogramScope::CleanUp); + } +} + +void HistogramScope::initializeGL() +{ + ScopeBase::initializeGL(); + + pipeline_secondary_ = CreateSecondaryShader(); + + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, + &HistogramScope::CleanUp, Qt::DirectConnection); +} + +void HistogramScope::AssertAdditionalTextures() +{ + if (!texture_row_sums_.IsCreated() + || texture_row_sums_.width() != width() + || texture_row_sums_.height() != height()) { + texture_row_sums_.Destroy(); + texture_row_sums_.Create(context(), VideoParams(width(), + height(), managed_tex().format())); + } +} + +void HistogramScope::CleanUp() +{ + makeCurrent(); + + pipeline_secondary_ = nullptr; + texture_row_sums_.Destroy(); + + doneCurrent(); +} + +OpenGLShaderPtr HistogramScope::CreateShader() +{ + OpenGLShaderPtr pipeline = OpenGLShader::Create(); + + pipeline->create(); + pipeline->addShaderFromSourceCode(QOpenGLShader::Vertex, + OpenGLShader::CodeDefaultVertex()); + pipeline->addShaderFromSourceCode(QOpenGLShader::Fragment, + Node::ReadFileAsString(":/shaders/rgbhistogram.frag")); + pipeline->link(); + + return pipeline; +} + +OpenGLShaderPtr HistogramScope::CreateSecondaryShader() +{ + OpenGLShaderPtr pipeline_secondary_ = OpenGLShader::Create(); + + pipeline_secondary_->create(); + pipeline_secondary_->addShaderFromSourceCode(QOpenGLShader::Vertex, + Node::ReadFileAsString(":/shaders/rgbhistogram.vert")); + pipeline_secondary_->addShaderFromSourceCode(QOpenGLShader::Fragment, + Node::ReadFileAsString(":/shaders/rgbhistogram_secondary.frag")); + pipeline_secondary_->link(); + + return pipeline_secondary_; +} + +void HistogramScope::DrawScope() +{ + float histogram_scale = 0.80f; + // This value is eyeballed for usefulness. Until we have a geometry + // shader approach, it is impossible to normalize against a peak + // sum of image values. + float histogram_base = 2.5f; + float histogram_power = 1.0f / histogram_base; + + pipeline()->bind(); + pipeline()->setUniformValue("ove_resolution", managed_tex().width(), + managed_tex().height()); + pipeline()->setUniformValue("ove_viewport", width(), height()); + pipeline()->setUniformValue("histogram_scale", histogram_scale); + pipeline()->release(); + + AssertAdditionalTextures(); + + framebuffer().Attach(&texture_row_sums_, true); + framebuffer().Bind(); + + managed_tex().Bind(); + + OpenGLRenderFunctions::Blit(pipeline()); + + managed_tex().Release(); + + framebuffer().Release(); + framebuffer().Detach(); + + pipeline_secondary_->bind(); + pipeline_secondary_->setUniformValue("ove_resolution", + texture_row_sums_.width(), texture_row_sums_.height()); + pipeline_secondary_->setUniformValue("ove_viewport", width(), height()); + pipeline_secondary_->setUniformValue("histogram_scale", histogram_scale); + pipeline_secondary_->setUniformValue("histogram_power", histogram_power); + pipeline_secondary_->release(); + + texture_row_sums_.Bind(); + + OpenGLRenderFunctions::Blit(pipeline_secondary_); + + texture_row_sums_.Release(); + + // Draw line overlays + QPainter p(this); + QFont font = p.font(); + font.setPixelSize(10); + QFontMetrics font_metrics = QFontMetrics(font); + QString label; + std::vector histogram_increments = { + 0.00, + 0.25, + 0.50, + 1.0 + }; + + int histogram_steps = histogram_increments.size(); + QVector histogram_lines(histogram_steps + 1); + int font_x_offset = 0; + int font_y_offset = font_metrics.capHeight() / 2.0f; + + p.setCompositionMode(QPainter::CompositionMode_Plus); + + p.setPen(QColor(0.0, 0.6 * 255.0, 0.0)); + p.setFont(font); + + float histogram_dim_x = ceil((width() - 1.0) * histogram_scale); + float histogram_dim_y = ceil((height() - 1.0) * histogram_scale); + float histogram_start_dim_x = + ((width() - 1.0) - histogram_dim_x) / 2.0f; + float histogram_start_dim_y = + ((height() - 1.0) - histogram_dim_y) / 2.0f; + float histogram_end_dim_x = (width() - 1.0) - histogram_start_dim_x; + + // for (int i=0; i <= histogram_steps; i++) { + for(std::vector::iterator it = histogram_increments.begin(); + it != histogram_increments.end(); it++) { + histogram_lines[it - histogram_increments.begin()].setLine( + histogram_start_dim_x, + (histogram_dim_y * pow(1.0 - *it, histogram_base)) + + histogram_start_dim_y, + histogram_end_dim_x, + (histogram_dim_y * pow(1.0 - *it, histogram_base)) + + histogram_start_dim_y); + label = QString::number( + *it * 100, 'f', 1) + "%"; + font_x_offset = QFontMetricsWidth(font_metrics, label) + 4; + + p.drawText( + histogram_start_dim_x - font_x_offset, + (histogram_dim_y * pow(1.0 - *it, histogram_base)) + + histogram_start_dim_y + font_y_offset, label); + } + p.drawLines(histogram_lines); +} + OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/histogram/histogram.h b/app/widget/scope/histogram/histogram.h index 625074a89..70751355f 100644 --- a/app/widget/scope/histogram/histogram.h +++ b/app/widget/scope/histogram/histogram.h @@ -31,11 +31,24 @@ class HistogramScope : public ScopeBase public: HistogramScope(QWidget* parent = nullptr); + virtual ~HistogramScope() override; + protected: - //virtual OpenGLShaderPtr CreateShader() override; + virtual void initializeGL() override; - //virtual void DrawScope() override; + virtual OpenGLShaderPtr CreateShader() override; + OpenGLShaderPtr CreateSecondaryShader(); + void AssertAdditionalTextures(); + + virtual void DrawScope() override; + +private: + OpenGLShaderPtr pipeline_secondary_; + OpenGLTexture texture_row_sums_; + +private slots: + void CleanUp(); }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/scopebase/scopebase.cpp b/app/widget/scope/scopebase/scopebase.cpp index c58b9e8bd..e3b65d48c 100644 --- a/app/widget/scope/scopebase/scopebase.cpp +++ b/app/widget/scope/scopebase/scopebase.cpp @@ -68,16 +68,6 @@ void ScopeBase::DrawScope() managed_tex().Release(); } -OpenGLShaderPtr ScopeBase::pipeline() -{ - return pipeline_; -} - -OpenGLTexture &ScopeBase::managed_tex() -{ - return managed_tex_; -} - void ScopeBase::UploadTextureFromBuffer() { if (!isVisible()) { diff --git a/app/widget/scope/scopebase/scopebase.h b/app/widget/scope/scopebase/scopebase.h index 3098af212..41e2edefc 100644 --- a/app/widget/scope/scopebase/scopebase.h +++ b/app/widget/scope/scopebase/scopebase.h @@ -51,9 +51,20 @@ protected: virtual void DrawScope(); - OpenGLShaderPtr pipeline(); + OpenGLShaderPtr pipeline() + { + return pipeline_; + } - OpenGLTexture& managed_tex(); + OpenGLTexture& managed_tex() + { + return managed_tex_; + } + + OpenGLFramebuffer& framebuffer() + { + return framebuffer_; + } private: void UploadTextureFromBuffer();