Implement color LUT v0.4 features

This commit is contained in:
2026-06-07 00:24:08 +08:00
parent 0daad22129
commit cac5a40b58
25 changed files with 996 additions and 2 deletions
+2
View File
@@ -18,6 +18,8 @@ add_subdirectory(colormanager)
add_subdirectory(displaytransform)
add_subdirectory(ociobase)
add_subdirectory(ociogradingtransformlinear)
add_subdirectory(ociolut)
add_subdirectory(threewaycolor)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
+14
View File
@@ -0,0 +1,14 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2026 Oak 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.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/color/ociolut/ociolut.cpp
node/color/ociolut/ociolut.h
PARENT_SCOPE
)
+138
View File
@@ -0,0 +1,138 @@
/***
Oak - Non-Linear Video Editor
Copyright (C) 2026 Oak 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 "ociolut.h"
#include <QFileInfo>
#include "node/color/colormanager/colormanager.h"
namespace olive
{
const QString OCIOLutNode::kFileInput = QStringLiteral("lut_file_in");
const QString OCIOLutNode::kDirectionInput = QStringLiteral("lut_dir_in");
#define super OCIOBaseNode
OCIOLutNode::OCIOLutNode()
{
AddInput(kFileInput, NodeValue::kFile, QString(),
InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
SetInputProperty(
kFileInput, QStringLiteral("filter"),
tr("LUT Files (*.cube *.3dl);;Cube LUT (*.cube);;3DL LUT (*.3dl);;All Files (*)"));
SetInputProperty(kFileInput, QStringLiteral("placeholder"),
tr("Select a .cube or .3dl LUT file"));
AddInput(kDirectionInput, NodeValue::kCombo, 0,
InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
}
QString OCIOLutNode::Name() const
{
return tr("OCIO LUT");
}
QString OCIOLutNode::id() const
{
return QStringLiteral("org.olivevideoeditor.Olive.ociolut");
}
QVector<Node::CategoryID> OCIOLutNode::Category() const
{
return { kCategoryColor };
}
QString OCIOLutNode::Description() const
{
return tr("Applies a LUT file through OpenColorIO.");
}
void OCIOLutNode::Retranslate()
{
super::Retranslate();
SetInputName(kTextureInput, tr("Input"));
SetInputName(kFileInput, tr("LUT File"));
SetInputName(kDirectionInput, tr("Direction"));
SetComboBoxStrings(kDirectionInput, { tr("Forward"), tr("Inverse") });
}
void OCIOLutNode::InputValueChangedEvent(const QString &input, int element)
{
Q_UNUSED(element)
if (input == kFileInput || input == kDirectionInput) {
GenerateProcessor();
}
}
void OCIOLutNode::ConfigChanged()
{
GenerateProcessor();
}
void OCIOLutNode::GenerateProcessor()
{
if (!manager()) {
set_processor(nullptr);
return;
}
const QString path = GetStandardValue(kFileInput).toString();
if (path.isEmpty()) {
set_processor(nullptr);
return;
}
const QFileInfo info(path);
if (!info.exists() || !info.isFile()) {
qWarning() << "OCIO LUT file does not exist:" << path;
set_processor(nullptr);
return;
}
const QString suffix = info.suffix();
if (!OCIO::FileTransform::IsFormatExtensionSupported(suffix.toUtf8().constData())) {
qWarning() << "Unsupported OCIO LUT file extension:" << path;
set_processor(nullptr);
return;
}
try {
OCIO::FileTransformRcPtr transform = OCIO::FileTransform::Create();
transform->setSrc(path.toUtf8().constData());
transform->setInterpolation(OCIO::INTERP_LINEAR);
transform->setDirection(
static_cast<ColorProcessor::Direction>(
GetStandardValue(kDirectionInput).toInt()) == ColorProcessor::kNormal
? OCIO::TRANSFORM_DIR_FORWARD
: OCIO::TRANSFORM_DIR_INVERSE);
set_processor(ColorProcessor::Create(
manager()->GetConfig()->getProcessor(transform)));
} catch (const OCIO::Exception &e) {
qWarning() << "OCIO LUT processor error:" << e.what();
set_processor(nullptr);
}
}
} // namespace olive
+58
View File
@@ -0,0 +1,58 @@
/***
Oak - Non-Linear Video Editor
Copyright (C) 2026 Oak 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 OCIOLUTNODE_H
#define OCIOLUTNODE_H
#include "node/color/ociobase/ociobase.h"
#include "render/colorprocessor.h"
namespace olive
{
class OCIOLutNode : public OCIOBaseNode {
Q_OBJECT
public:
OCIOLutNode();
NODE_DEFAULT_FUNCTIONS(OCIOLutNode)
virtual QString Name() const override;
virtual QString id() const override;
virtual QVector<CategoryID> Category() const override;
virtual QString Description() const override;
virtual void Retranslate() override;
virtual void InputValueChangedEvent(const QString &input,
int element) override;
static const QString kFileInput;
static const QString kDirectionInput;
protected slots:
virtual void ConfigChanged() override;
private:
void GenerateProcessor();
};
} // namespace olive
#endif // OCIOLUTNODE_H
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2022 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}
node/color/threewaycolor/threewaycolor.h
node/color/threewaycolor/threewaycolor.cpp
PARENT_SCOPE
)
@@ -0,0 +1,124 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2026 mikesolar
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 "threewaycolor.h"
#include <QVector3D>
#include "node/project.h"
#include "widget/slider/floatslider.h"
namespace olive
{
#define super Node
const QString ThreeWayColorNode::kTextureInput = QStringLiteral("tex_in");
const QString ThreeWayColorNode::kShadowsColorInput =
QStringLiteral("shadows_color_in");
const QString ThreeWayColorNode::kMidtonesColorInput =
QStringLiteral("midtones_color_in");
const QString ThreeWayColorNode::kHighlightsColorInput =
QStringLiteral("highlights_color_in");
const QString ThreeWayColorNode::kShadowsAmountInput =
QStringLiteral("shadows_amount_in");
const QString ThreeWayColorNode::kMidtonesAmountInput =
QStringLiteral("midtones_amount_in");
const QString ThreeWayColorNode::kHighlightsAmountInput =
QStringLiteral("highlights_amount_in");
const QString ThreeWayColorNode::kLumaCoefficientsInput =
QStringLiteral("luma_coefficients_in");
ThreeWayColorNode::ThreeWayColorNode()
{
AddInput(kTextureInput, NodeValue::kTexture,
InputFlags(kInputFlagNotKeyframable));
const QVariant neutral = QVariant::fromValue(Color(0.5, 0.5, 0.5, 1.0));
AddInput(kShadowsColorInput, NodeValue::kColor, neutral);
AddInput(kMidtonesColorInput, NodeValue::kColor, neutral);
AddInput(kHighlightsColorInput, NodeValue::kColor, neutral);
AddInput(kShadowsAmountInput, NodeValue::kFloat, 1.0);
AddInput(kMidtonesAmountInput, NodeValue::kFloat, 1.0);
AddInput(kHighlightsAmountInput, NodeValue::kFloat, 1.0);
const QString min = QStringLiteral("min");
const QString view = QStringLiteral("view");
SetInputProperty(kShadowsAmountInput, min, 0.0);
SetInputProperty(kMidtonesAmountInput, min, 0.0);
SetInputProperty(kHighlightsAmountInput, min, 0.0);
SetInputProperty(kShadowsAmountInput, view, FloatSlider::kPercentage);
SetInputProperty(kMidtonesAmountInput, view, FloatSlider::kPercentage);
SetInputProperty(kHighlightsAmountInput, view, FloatSlider::kPercentage);
SetEffectInput(kTextureInput);
SetFlag(kVideoEffect);
}
void ThreeWayColorNode::Retranslate()
{
super::Retranslate();
SetInputName(kTextureInput, tr("Input"));
SetInputName(kShadowsColorInput, tr("Shadows"));
SetInputName(kMidtonesColorInput, tr("Midtones"));
SetInputName(kHighlightsColorInput, tr("Highlights"));
SetInputName(kShadowsAmountInput, tr("Shadows Amount"));
SetInputName(kMidtonesAmountInput, tr("Midtones Amount"));
SetInputName(kHighlightsAmountInput, tr("Highlights Amount"));
}
ShaderCode
ThreeWayColorNode::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(request)
return ShaderCode(
FileFunctions::ReadFileAsString(":/shaders/threewaycolor.frag"));
}
void ThreeWayColorNode::Value(const NodeValueRow &value,
const NodeGlobals &globals,
NodeValueTable *table) const
{
Q_UNUSED(globals)
if (TexturePtr tex = value[kTextureInput].toTexture()) {
ShaderJob job(value);
double luma_coeffs[3] = { 0.0, 0.0, 0.0 };
if (project() && project()->color_manager()) {
project()->color_manager()->GetDefaultLumaCoefs(luma_coeffs);
} else {
luma_coeffs[0] = 0.2126;
luma_coeffs[1] = 0.7152;
luma_coeffs[2] = 0.0722;
}
job.Insert(kLumaCoefficientsInput,
NodeValue(NodeValue::kVec3,
QVector3D(luma_coeffs[0], luma_coeffs[1],
luma_coeffs[2])));
table->Push(NodeValue::kTexture, tex->toJob(job), this);
}
}
}
@@ -0,0 +1,77 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2026 mikesolar
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 THREEWAYCOLORNODE_H
#define THREEWAYCOLORNODE_H
#include "node/node.h"
namespace olive
{
class ThreeWayColorNode : public Node {
Q_OBJECT
public:
ThreeWayColorNode();
NODE_DEFAULT_FUNCTIONS(ThreeWayColorNode)
virtual QString Name() const override
{
return tr("Three-Way Color");
}
virtual QString id() const override
{
return QStringLiteral("org.olivevideoeditor.Olive.threewaycolor");
}
virtual QVector<CategoryID> Category() const override
{
return { kCategoryColor };
}
virtual QString Description() const override
{
return tr("Adjusts shadows, midtones, and highlights separately.");
}
virtual void Retranslate() override;
virtual ShaderCode
GetShaderCode(const ShaderRequest &request) const override;
virtual void Value(const NodeValueRow &value, const NodeGlobals &globals,
NodeValueTable *table) const override;
static const QString kTextureInput;
static const QString kShadowsColorInput;
static const QString kMidtonesColorInput;
static const QString kHighlightsColorInput;
static const QString kShadowsAmountInput;
static const QString kMidtonesAmountInput;
static const QString kHighlightsAmountInput;
static const QString kLumaCoefficientsInput;
};
}
#endif // THREEWAYCOLORNODE_H
+6
View File
@@ -32,6 +32,8 @@
#include "block/transition/diptocolor/diptocolortransition.h"
#include "color/displaytransform/displaytransform.h"
#include "color/ociogradingtransformlinear/ociogradingtransformlinear.h"
#include "color/ociolut/ociolut.h"
#include "color/threewaycolor/threewaycolor.h"
#include "common/Current.h"
#include "distort/cornerpin/cornerpindistortnode.h"
#include "distort/crop/cropdistortnode.h"
@@ -358,6 +360,10 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new DisplayTransformNode();
case kOCIOGradingTransformLinear:
return new OCIOGradingTransformLinearNode();
case kOCIOLut:
return new OCIOLutNode();
case kThreeWayColor:
return new ThreeWayColorNode();
case kChromaKey:
return new ChromaKeyNode();
case kMaskDistort:
+2
View File
@@ -73,6 +73,8 @@ public:
kCornerPinDistort,
kDisplayTransform,
kOCIOGradingTransformLinear,
kOCIOLut,
kThreeWayColor,
kChromaKey,
kMaskDistort,
kDropShadowFilter,
+8
View File
@@ -59,6 +59,10 @@ ScopePanel::ScopePanel()
waveform_view_ = new WaveformScope();
stack_->addWidget(waveform_view_);
// Create vectorscope
vectorscope_ = new VectorscopeScope();
stack_->addWidget(vectorscope_);
// Create histogram
histogram_ = new HistogramScope();
stack_->addWidget(histogram_);
@@ -81,6 +85,8 @@ QString ScopePanel::TypeToName(ScopePanel::Type t)
switch (t) {
case kTypeWaveform:
return tr("Waveform");
case kTypeVectorscope:
return tr("Vectorscope");
case kTypeHistogram:
return tr("Histogram");
case kTypeCount:
@@ -124,12 +130,14 @@ void ScopePanel::SetViewerPanel(ViewerPanelBase *vp)
void ScopePanel::SetReferenceBuffer(TexturePtr frame)
{
histogram_->SetBuffer(frame);
vectorscope_->SetBuffer(frame);
waveform_view_->SetBuffer(frame);
}
void ScopePanel::SetColorManager(ColorManager *manager)
{
histogram_->ConnectColorManager(manager);
vectorscope_->ConnectColorManager(manager);
waveform_view_->ConnectColorManager(manager);
}
+4
View File
@@ -28,6 +28,7 @@
#include "panel/panel.h"
#include "panel/viewer/viewerbase.h"
#include "widget/scope/histogram/histogram.h"
#include "widget/scope/vectorscope/vectorscope.h"
#include "widget/scope/waveform/waveform.h"
namespace olive
@@ -38,6 +39,7 @@ class ScopePanel : public PanelWidget {
public:
enum Type {
kTypeWaveform,
kTypeVectorscope,
kTypeHistogram,
kTypeCount
@@ -73,6 +75,8 @@ private:
WaveformScope *waveform_view_;
VectorscopeScope *vectorscope_;
HistogramScope *histogram_;
ViewerPanelBase *viewer_;
+48
View File
@@ -0,0 +1,48 @@
uniform sampler2D ove_maintex;
uniform vec2 viewport;
uniform vec3 luma_coeffs;
uniform float vectorscope_gain;
uniform float vectorscope_point_radius;
uniform float vectorscope_intensity;
uniform float vectorscope_sample_grid;
in vec2 ove_texcoord;
out vec4 frag_color;
vec2 rgb_to_vectorscope(vec3 rgb)
{
float y = dot(rgb, luma_coeffs);
float cb = (rgb.b - y) / max(2.0 * (1.0 - luma_coeffs.b), 0.0001);
float cr = (rgb.r - y) / max(2.0 * (1.0 - luma_coeffs.r), 0.0001);
return vec2(cr, cb) * vectorscope_gain + vec2(0.5);
}
void main(void)
{
float grid = max(vectorscope_sample_grid, 1.0);
float point_radius = vectorscope_point_radius / max(min(viewport.x, viewport.y), 1.0);
vec3 accumulated = vec3(0.0);
for (int y = 0; y < 64; y++) {
if (float(y) >= grid) {
break;
}
for (int x = 0; x < 64; x++) {
if (float(x) >= grid) {
break;
}
vec2 sample_uv = (vec2(float(x), float(y)) + vec2(0.5)) / grid;
vec3 sample_rgb = clamp(texture(ove_maintex, sample_uv).rgb, 0.0, 1.0);
vec2 scope_point = rgb_to_vectorscope(sample_rgb);
float distance_to_point = distance(ove_texcoord, scope_point);
float contribution = 1.0 - smoothstep(point_radius, point_radius * 2.0, distance_to_point);
accumulated += sample_rgb * contribution * vectorscope_intensity;
}
}
frag_color = vec4(clamp(accumulated, 0.0, 1.0), 1.0);
}
+25
View File
@@ -0,0 +1,25 @@
uniform float vectorscope_scale;
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()
{
mat4 transform = mat4(1.0);
transform *= scale_mat4(vec3(vectorscope_scale, vectorscope_scale, 1.0));
gl_Position = transform * a_position;
ove_texcoord = a_texcoord;
}
+35
View File
@@ -0,0 +1,35 @@
uniform sampler2D tex_in;
uniform vec4 shadows_color_in;
uniform vec4 midtones_color_in;
uniform vec4 highlights_color_in;
uniform float shadows_amount_in;
uniform float midtones_amount_in;
uniform float highlights_amount_in;
uniform vec3 luma_coefficients_in;
in vec2 ove_texcoord;
out vec4 frag_color;
vec3 color_offset(vec4 control, float amount)
{
return (control.rgb - vec3(0.5)) * 2.0 * amount;
}
void main(void)
{
vec4 source = texture(tex_in, ove_texcoord);
float luma = clamp(dot(source.rgb, luma_coefficients_in), 0.0, 1.0);
float shadow_weight = smoothstep(0.75, 0.0, luma);
float highlight_weight = smoothstep(0.25, 1.0, luma);
float midtone_weight = clamp(1.0 - abs(luma - 0.5) * 2.0, 0.0, 1.0);
vec3 adjustment =
color_offset(shadows_color_in, shadows_amount_in) * shadow_weight +
color_offset(midtones_color_in, midtones_amount_in) * midtone_weight +
color_offset(highlights_color_in, highlights_amount_in) * highlight_weight;
vec3 graded = source.rgb + adjustment * source.rgb * (1.0 - source.rgb);
frag_color = vec4(clamp(graded, 0.0, 1.0), source.a);
}
+2 -1
View File
@@ -59,7 +59,8 @@ void FileField::BrowseBtnClicked()
if (directory_mode_) {
s = QFileDialog::getExistingDirectory(this, tr("Open Directory"));
} else {
s = QFileDialog::getOpenFileName(this, tr("Open File"));
s = QFileDialog::getOpenFileName(this, tr("Open File"), QString(),
name_filter_);
}
if (!s.isEmpty()) {
+7
View File
@@ -53,6 +53,11 @@ public:
directory_mode_ = e;
}
void SetNameFilter(const QString &filter)
{
name_filter_ = filter;
}
signals:
void FilenameChanged(const QString &filename);
@@ -63,6 +68,8 @@ private:
bool directory_mode_;
QString name_filter_;
private slots:
void BrowseBtnClicked();
@@ -949,6 +949,8 @@ void NodeParamViewWidgetBridge::SetProperty(const QString &key,
ff->SetPlaceholder(value.toString());
} else if (key == QStringLiteral("directory")) {
ff->SetDirectoryMode(value.toBool());
} else if (key == QStringLiteral("filter")) {
ff->SetNameFilter(value.toString());
}
}
+1
View File
@@ -16,6 +16,7 @@
add_subdirectory(histogram)
add_subdirectory(scopebase)
add_subdirectory(vectorscope)
add_subdirectory(waveform)
set(OLIVE_SOURCES
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2022 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/vectorscope/vectorscope.h
widget/scope/vectorscope/vectorscope.cpp
PARENT_SCOPE
)
@@ -0,0 +1,132 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2026 mikesolar
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 "vectorscope.h"
#include <QPainter>
#include <QtMath>
#include <QVector2D>
#include <QVector3D>
#include "common/qtutils.h"
#include "node/node.h"
namespace olive
{
#define super ScopeBase
VectorscopeScope::VectorscopeScope(QWidget *parent)
: super(parent)
{
}
ShaderCode VectorscopeScope::GenerateShaderCode()
{
return ShaderCode(
FileFunctions::ReadFileAsString(":/shaders/rgbvectorscope.frag"),
FileFunctions::ReadFileAsString(":/shaders/rgbvectorscope.vert"));
}
void VectorscopeScope::DrawScope(TexturePtr managed_tex, QVariant pipeline)
{
float vectorscope_scale = 0.80f;
float vectorscope_gain = 1.45f;
float vectorscope_point_radius = 1.75f;
float vectorscope_intensity = 0.035f;
float vectorscope_sample_grid = 28.0f;
ShaderJob job;
job.Insert(QStringLiteral("viewport"),
NodeValue(NodeValue::kVec2, QVector2D(width(), height())));
double luma_coeffs[3] = { 0.0f, 0.0f, 0.0f };
color_manager()->GetDefaultLumaCoefs(luma_coeffs);
job.Insert(
QStringLiteral("luma_coeffs"),
NodeValue(NodeValue::kVec3,
QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2])));
job.Insert(QStringLiteral("vectorscope_scale"),
NodeValue(NodeValue::kFloat, vectorscope_scale));
job.Insert(QStringLiteral("vectorscope_gain"),
NodeValue(NodeValue::kFloat, vectorscope_gain));
job.Insert(QStringLiteral("vectorscope_point_radius"),
NodeValue(NodeValue::kFloat, vectorscope_point_radius));
job.Insert(QStringLiteral("vectorscope_intensity"),
NodeValue(NodeValue::kFloat, vectorscope_intensity));
job.Insert(QStringLiteral("vectorscope_sample_grid"),
NodeValue(NodeValue::kFloat, vectorscope_sample_grid));
job.Insert(QStringLiteral("ove_maintex"),
NodeValue(NodeValue::kTexture,
QVariant::fromValue(managed_tex)));
renderer()->Blit(pipeline, job, GetViewportParams());
QPainter p(paint_device());
QFont font = p.font();
font.setPixelSize(10);
QFontMetrics font_metrics = QFontMetrics(font);
p.setCompositionMode(QPainter::CompositionMode_Plus);
p.setPen(QColor(0, 153, 0));
p.setFont(font);
float scope_size = qMin(width(), height()) * vectorscope_scale;
QPointF center(width() * 0.5, height() * 0.5);
float radius = scope_size * 0.5;
p.drawEllipse(center, radius, radius);
p.drawLine(QPointF(center.x() - radius, center.y()),
QPointF(center.x() + radius, center.y()));
p.drawLine(QPointF(center.x(), center.y() - radius),
QPointF(center.x(), center.y() + radius));
struct Target {
const char *label;
float angle;
};
const Target targets[] = {
{ "R", 0.0f }, { "Mg", 60.0f }, { "B", 120.0f },
{ "Cy", 180.0f }, { "G", 240.0f }, { "Yl", 300.0f },
};
const float label_radius = radius + 12.0f;
const float marker_radius = radius * 0.72f;
constexpr float kPi = 3.14159265358979323846f;
for (const Target &target : targets) {
float radians = target.angle * kPi / 180.0f;
QPointF direction(qCos(radians), -qSin(radians));
QPointF marker = center + direction * marker_radius;
QPointF label_pos = center + direction * label_radius;
QString label = QString::fromUtf8(target.label);
p.drawEllipse(marker, 3.0, 3.0);
p.drawText(label_pos.x() -
QtUtils::QFontMetricsWidth(font_metrics, label) * 0.5,
label_pos.y() + font_metrics.capHeight() * 0.5, label);
}
}
}
@@ -0,0 +1,45 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 Olive Team
Modifications Copyright (C) 2026 mikesolar
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 VECTORSCOPESCOPE_H
#define VECTORSCOPESCOPE_H
#include "widget/scope/scopebase/scopebase.h"
namespace olive
{
class VectorscopeScope : public ScopeBase {
Q_OBJECT
public:
VectorscopeScope(QWidget *parent = nullptr);
MANAGEDDISPLAYWIDGET_DEFAULT_DESTRUCTOR(VectorscopeScope)
protected:
virtual ShaderCode GenerateShaderCode() override;
virtual void DrawScope(TexturePtr managed_tex, QVariant pipeline) override;
};
}
#endif // VECTORSCOPESCOPE_H