From f3290003be6c8c094ba7ea3a79135819ed040f9b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 2 Apr 2020 13:02:30 +1100 Subject: [PATCH] colordialog/wheel/gradient: implemented basic UI components for color picking --- app/dialog/color/CMakeLists.txt | 4 + app/dialog/color/colordialog.cpp | 58 +++++++++-- app/dialog/color/colordialog.h | 17 +++- app/dialog/color/colorpreviewbox.cpp | 31 ++++++ app/dialog/color/colorpreviewbox.h | 25 +++++ app/dialog/color/colorvalueswidget.cpp | 62 ++++++++++++ app/dialog/color/colorvalueswidget.h | 32 +++++++ app/node/param.h | 2 +- app/render/backend/opengl/openglproxy.cpp | 2 +- .../backend/opengl/openglrenderfunctions.cpp | 15 ++- .../backend/opengl/openglrenderfunctions.h | 2 + app/render/backend/opengl/openglshader.cpp | 7 +- app/render/backend/opengl/openglshader.h | 2 + app/render/color.cpp | 95 +++++++++++++++++++ app/render/color.h | 12 +++ app/shaders/colorgradient.frag | 25 +++++ app/shaders/colorwheel.frag | 91 ++++++++++++++++++ app/shaders/shaders.qrc | 2 + app/widget/colorbutton/colorbutton.cpp | 4 +- app/widget/colorbutton/colorbutton.h | 5 + app/widget/colorwheel/CMakeLists.txt | 4 + app/widget/colorwheel/colorgradientwidget.cpp | 57 +++++++++++ app/widget/colorwheel/colorgradientwidget.h | 33 +++++++ app/widget/colorwheel/colorswatchwidget.cpp | 61 ++++++++++++ app/widget/colorwheel/colorswatchwidget.h | 45 +++++++++ app/widget/colorwheel/colorwheelwidget.cpp | 83 ++++++++++++++-- app/widget/colorwheel/colorwheelwidget.h | 43 ++++++++- 27 files changed, 792 insertions(+), 27 deletions(-) create mode 100644 app/dialog/color/colorpreviewbox.cpp create mode 100644 app/dialog/color/colorpreviewbox.h create mode 100644 app/dialog/color/colorvalueswidget.cpp create mode 100644 app/dialog/color/colorvalueswidget.h create mode 100644 app/shaders/colorgradient.frag create mode 100644 app/shaders/colorwheel.frag create mode 100644 app/widget/colorwheel/colorgradientwidget.cpp create mode 100644 app/widget/colorwheel/colorgradientwidget.h create mode 100644 app/widget/colorwheel/colorswatchwidget.cpp create mode 100644 app/widget/colorwheel/colorswatchwidget.h diff --git a/app/dialog/color/CMakeLists.txt b/app/dialog/color/CMakeLists.txt index f36cd6b5d..e921d67cf 100644 --- a/app/dialog/color/CMakeLists.txt +++ b/app/dialog/color/CMakeLists.txt @@ -18,5 +18,9 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} dialog/color/colordialog.h dialog/color/colordialog.cpp + dialog/color/colorpreviewbox.h + dialog/color/colorpreviewbox.cpp + dialog/color/colorvalueswidget.h + dialog/color/colorvalueswidget.cpp PARENT_SCOPE ) diff --git a/app/dialog/color/colordialog.cpp b/app/dialog/color/colordialog.cpp index d96be4b71..be8cf1e3c 100644 --- a/app/dialog/color/colordialog.cpp +++ b/app/dialog/color/colordialog.cpp @@ -4,25 +4,71 @@ #include #include -#include "widget/colorwheel/colorwheelwidget.h" +#include "common/qtutils.h" -ColorDialog::ColorDialog(ColorManager* color_manager, QWidget *parent) : +ColorDialog::ColorDialog(ColorManager* color_manager, Color start, QWidget *parent) : QDialog(parent), color_manager_(color_manager) { + setWindowTitle(tr("Select Color")); + QVBoxLayout* layout = new QVBoxLayout(this); QSplitter* splitter = new QSplitter(Qt::Horizontal); layout->addWidget(splitter); - ColorWheelWidget* cww1 = new ColorWheelWidget(); - ColorWheelWidget* cww2 = new ColorWheelWidget(); + QWidget* wheel_area = new QWidget(); + QHBoxLayout* wheel_layout = new QHBoxLayout(wheel_area); + splitter->addWidget(wheel_area); - splitter->addWidget(cww1); - splitter->addWidget(cww2); + color_wheel_ = new ColorWheelGLWidget(); + wheel_layout->addWidget(color_wheel_); + + hsv_value_gradient_ = new ColorGradientGLWidget(Qt::Vertical); + hsv_value_gradient_->setFixedWidth(QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH"))); + wheel_layout->addWidget(hsv_value_gradient_); + + ColorValuesWidget* cvw = new ColorValuesWidget(); + splitter->addWidget(cvw); + + connect(color_wheel_, &ColorWheelGLWidget::ColorChanged, cvw, &ColorValuesWidget::SetColor); + connect(color_wheel_, &ColorWheelGLWidget::ColorChanged, this, &ColorDialog::UpdateValueGradient); + connect(color_wheel_, &ColorWheelGLWidget::DiameterChanged, hsv_value_gradient_, &ColorGradientGLWidget::setFixedHeight); + connect(hsv_value_gradient_, &ColorGradientGLWidget::ColorChanged, cvw, &ColorValuesWidget::SetColor); + connect(hsv_value_gradient_, &ColorGradientGLWidget::ColorChanged, this, &ColorDialog::UpdateWheelValue); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); layout->addWidget(buttons); + + UpdateValueGradient(start); + UpdateWheelValue(start); + cvw->SetColor(start); +} + +void ColorDialog::UpdateValueGradient(const Color &c) +{ + float h, s, v; + + c.toHsv(&h, &s, &v); + + Color start = Color::fromHsv(h, s, 1.0f); + Color end = Color::fromHsv(h, s, 0.0f); + + hsv_value_gradient_->SetColors(start, end); +} + +void ColorDialog::UpdateWheelValue(const Color &c) +{ + float h, s, v; + + c.toHsv(&h, &s, &v); + + color_wheel_->SetHSVValue(v); +} + +void ColorDialog::UpdateValueGradientSize(int diameter) +{ + hsv_value_gradient_->setFixedHeight(diameter); } diff --git a/app/dialog/color/colordialog.h b/app/dialog/color/colordialog.h index 19aa16678..06dfc1a22 100644 --- a/app/dialog/color/colordialog.h +++ b/app/dialog/color/colordialog.h @@ -3,17 +3,32 @@ #include +#include "colorvalueswidget.h" #include "render/color.h" #include "render/colormanager.h" +#include "widget/colorwheel/colorwheelwidget.h" +#include "widget/colorwheel/colorgradientwidget.h" class ColorDialog : public QDialog { + Q_OBJECT public: - ColorDialog(ColorManager* color_manager, QWidget* parent = nullptr); + ColorDialog(ColorManager* color_manager, Color start = Color(1.0f, 1.0f, 1.0f), QWidget* parent = nullptr); private: ColorManager* color_manager_; + ColorWheelGLWidget* color_wheel_; + + ColorGradientGLWidget* hsv_value_gradient_; + +private slots: + void UpdateValueGradient(const Color& c); + + void UpdateWheelValue(const Color& c); + + void UpdateValueGradientSize(int diameter); + }; #endif // COLORDIALOG_H diff --git a/app/dialog/color/colorpreviewbox.cpp b/app/dialog/color/colorpreviewbox.cpp new file mode 100644 index 000000000..4cd478c1b --- /dev/null +++ b/app/dialog/color/colorpreviewbox.cpp @@ -0,0 +1,31 @@ +#include "colorpreviewbox.h" + +#include + +ColorPreviewBox::ColorPreviewBox(QWidget *parent) : + QWidget(parent) +{ +} + +void ColorPreviewBox::SetColor(const Color &c) +{ + color_ = c; + update(); +} + +void ColorPreviewBox::paintEvent(QPaintEvent *e) +{ + QWidget::paintEvent(e); + + QPainter p(this); + + p.setPen(Qt::black); + + QColor c; + c.setRedF(color_.red()); + c.setGreenF(color_.green()); + c.setBlueF(color_.blue()); + p.setBrush(c); + + p.drawRect(rect()); +} diff --git a/app/dialog/color/colorpreviewbox.h b/app/dialog/color/colorpreviewbox.h new file mode 100644 index 000000000..a6d6fb6a0 --- /dev/null +++ b/app/dialog/color/colorpreviewbox.h @@ -0,0 +1,25 @@ +#ifndef COLORPREVIEWBOX_H +#define COLORPREVIEWBOX_H + +#include + +#include "render/color.h" + +class ColorPreviewBox : public QWidget +{ + Q_OBJECT +public: + ColorPreviewBox(QWidget* parent = nullptr); + +public slots: + void SetColor(const Color& c); + +protected: + virtual void paintEvent(QPaintEvent* e) override; + +private: + Color color_; + +}; + +#endif // COLORPREVIEWBOX_H diff --git a/app/dialog/color/colorvalueswidget.cpp b/app/dialog/color/colorvalueswidget.cpp new file mode 100644 index 000000000..7d5042d62 --- /dev/null +++ b/app/dialog/color/colorvalueswidget.cpp @@ -0,0 +1,62 @@ +#include "colorvalueswidget.h" + +#include + +ColorValuesWidget::ColorValuesWidget(QWidget *parent) : + QWidget(parent) +{ + QGridLayout* layout = new QGridLayout(this); + + int row = 0; + + layout->addWidget(new QLabel(tr("Preview")), row, 0); + + preview_ = new ColorPreviewBox(); + preview_->setFixedHeight(fontMetrics().height() * 3 / 2); + layout->addWidget(preview_, row, 1); + + row++; + + layout->addWidget(new QLabel(tr("Red")), row, 0); + + red_slider_ = CreateColorSlider(); + layout->addWidget(red_slider_, row, 1); + + row++; + + layout->addWidget(new QLabel(tr("Green")), row, 0); + + green_slider_ = CreateColorSlider(); + layout->addWidget(green_slider_, row, 1); + + row++; + + layout->addWidget(new QLabel(tr("Blue")), row, 0); + + blue_slider_ = CreateColorSlider(); + layout->addWidget(blue_slider_, row, 1); +} + +Color ColorValuesWidget::GetColor() const +{ + return Color(red_slider_->GetValue(), + green_slider_->GetValue(), + blue_slider_->GetValue()); +} + +void ColorValuesWidget::SetColor(const Color &c) +{ + red_slider_->SetValue(c.red()); + green_slider_->SetValue(c.green()); + blue_slider_->SetValue(c.blue()); + preview_->SetColor(c); +} + +FloatSlider *ColorValuesWidget::CreateColorSlider() +{ + FloatSlider* fs = new FloatSlider(); + fs->SetMinimum(0); + fs->SetDragMultiplier(0.01); + fs->SetMaximum(1); + return fs; +} diff --git a/app/dialog/color/colorvalueswidget.h b/app/dialog/color/colorvalueswidget.h new file mode 100644 index 000000000..aa495f5da --- /dev/null +++ b/app/dialog/color/colorvalueswidget.h @@ -0,0 +1,32 @@ +#ifndef COLORVALUESWIDGET_H +#define COLORVALUESWIDGET_H + +#include + +#include "colorpreviewbox.h" +#include "render/color.h" +#include "widget/slider/floatslider.h" + +class ColorValuesWidget : public QWidget +{ + Q_OBJECT +public: + ColorValuesWidget(QWidget* parent = nullptr); + + Color GetColor() const; + +public slots: + void SetColor(const Color& c); + +private: + static FloatSlider* CreateColorSlider(); + + ColorPreviewBox* preview_; + + FloatSlider* red_slider_; + FloatSlider* green_slider_; + FloatSlider* blue_slider_; + +}; + +#endif // COLORVALUESWIDGET_H diff --git a/app/node/param.h b/app/node/param.h index 28bfb6766..fb4399004 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -93,7 +93,7 @@ public: /** * Floating-point type * - * Resolves to `QRgba64`. + * Resolves to `Color`. * * Colors passed around the nodes should always be in reference space and preferably use */ diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index 4b4d3702b..2083f6695 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -205,7 +205,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, c vert_code = OpenGLShader::CodeDefaultVertex(); } - shader = std::make_shared(); + shader = OpenGLShader::Create(); shader->create(); shader->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_code); shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_code); diff --git a/app/render/backend/opengl/openglrenderfunctions.cpp b/app/render/backend/opengl/openglrenderfunctions.cpp index c315eb8e9..ebad69961 100644 --- a/app/render/backend/opengl/openglrenderfunctions.cpp +++ b/app/render/backend/opengl/openglrenderfunctions.cpp @@ -132,7 +132,13 @@ GLenum OpenGLRenderFunctions::GetPixelType(const PixelFormat::Format &format) return GL_INVALID_VALUE; } -void OpenGLRenderFunctions::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix4x4 matrix) { +void OpenGLRenderFunctions::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix4x4 matrix) +{ + Blit(pipeline.get(), flipped, matrix); +} + +void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x4 matrix) +{ // FIXME: is currentContext() reliable here? QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions(); @@ -181,7 +187,12 @@ void OpenGLRenderFunctions::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix m_vao.destroy(); } -void OpenGLRenderFunctions::OCIOBlit(OpenGLShaderPtr pipeline, +void OpenGLRenderFunctions::OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped, QMatrix4x4 matrix) +{ + OCIOBlit(pipeline.get(), lut, flipped, matrix); +} + +void OpenGLRenderFunctions::OCIOBlit(OpenGLShader *pipeline, GLuint lut, bool flipped, QMatrix4x4 matrix) diff --git a/app/render/backend/opengl/openglrenderfunctions.h b/app/render/backend/opengl/openglrenderfunctions.h index 22ff0c1ca..cdd5d0769 100644 --- a/app/render/backend/opengl/openglrenderfunctions.h +++ b/app/render/backend/opengl/openglrenderfunctions.h @@ -46,8 +46,10 @@ public: * Transformation matrix to use when drawing (defaults to no transform) */ static void Blit(OpenGLShaderPtr pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); + static void Blit(OpenGLShader* pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); static void OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); + static void OCIOBlit(OpenGLShader* pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); static void PrepareToDraw(QOpenGLFunctions* f); diff --git a/app/render/backend/opengl/openglshader.cpp b/app/render/backend/opengl/openglshader.cpp index 13d2d8fd1..4ace5e9ab 100644 --- a/app/render/backend/opengl/openglshader.cpp +++ b/app/render/backend/opengl/openglshader.cpp @@ -7,9 +7,14 @@ OpenGLShader::OpenGLShader() } +OpenGLShaderPtr OpenGLShader::Create() +{ + return std::make_shared(); +} + OpenGLShaderPtr OpenGLShader::CreateDefault(const QString &function_name, const QString &shader_code) { - OpenGLShaderPtr program = std::make_shared(); + OpenGLShaderPtr program = Create(); // Add shaders to program program->addShaderFromSourceCode(QOpenGLShader::Vertex, CodeDefaultVertex()); diff --git a/app/render/backend/opengl/openglshader.h b/app/render/backend/opengl/openglshader.h index f1d4c56db..ea77db482 100644 --- a/app/render/backend/opengl/openglshader.h +++ b/app/render/backend/opengl/openglshader.h @@ -17,6 +17,8 @@ class OpenGLShader : public QOpenGLShaderProgram { public: OpenGLShader(); + static OpenGLShaderPtr Create(); + static OpenGLShaderPtr CreateDefault(const QString &function_name = QString(), const QString &shader_code = QString()); diff --git a/app/render/color.cpp b/app/render/color.cpp index 3ad4f25c2..4ddb87005 100644 --- a/app/render/color.cpp +++ b/app/render/color.cpp @@ -1,5 +1,46 @@ #include "color.h" +Color Color::fromHsv(const float &h, const float &s, const float &v) +{ + float C = s * v; + float X = C * (1.0f - abs(fmod(h / 60.0f, 2.0f) - 1.0f)); + float m = v - C; + float Rs, Gs, Bs; + + if(h >= 0.0f && h < 60.0f) { + Rs = C; + Gs = X; + Bs = 0.0f; + } + else if(h >= 60.0f && h < 120.0f) { + Rs = X; + Gs = C; + Bs = 0.0f; + } + else if(h >= 120.0f && h < 180.0f) { + Rs = 0.0f; + Gs = C; + Bs = X; + } + else if(h >= 180.0f && h < 240.0f) { + Rs = 0.0f; + Gs = X; + Bs = C; + } + else if(h >= 240.0f && h < 300.0f) { + Rs = X; + Gs = 0.0f; + Bs = C; + } + else { + Rs = C; + Gs = 0.0f; + Bs = X; + } + + return Color(Rs + m, Gs + m, Bs + m); +} + Color::Color(const char *data, const PixelFormat::Format &format) { OIIO::convert_type(PixelFormat::GetOIIOTypeDesc(format), @@ -12,3 +53,57 @@ Color::Color(const char *data, const PixelFormat::Format &format) set_alpha(1.0f); } } + +void Color::toHsv(float *hue, float *sat, float *val) const +{ + float fCMax = qMax(qMax(red(), green()), blue()); + float fCMin = qMin(qMin(red(), green()), blue()); + float fDelta = fCMax - fCMin; + + if(fDelta > 0) { + if(fCMax == red()) { + *hue = 60 * (fmod(((green() - blue()) / fDelta), 6)); + } else if(fCMax == green()) { + *hue = 60 * (((blue() - red()) / fDelta) + 2); + } else if(fCMax == blue()) { + *hue = 60 * (((red() - green()) / fDelta) + 4); + } + + if(fCMax > 0) { + *sat = fDelta / fCMax; + } else { + *sat = 0; + } + + *val = fCMax; + } else { + *hue = 0; + *sat = 0; + *val = fCMax; + } + + if(*hue < 0) { + *hue = 360 + *hue; + } +} + +float Color::hue() const +{ + float h, s, v; + toHsv(&h, &s, &v); + return h; +} + +float Color::saturation() const +{ + float h, s, v; + toHsv(&h, &s, &v); + return s; +} + +float Color::value() const +{ + float h, s, v; + toHsv(&h, &s, &v); + return v; +} diff --git a/app/render/color.h b/app/render/color.h index fec812237..abc35071d 100644 --- a/app/render/color.h +++ b/app/render/color.h @@ -25,6 +25,13 @@ public: data_[3] = a; } + /** + * @brief Creates a Color struct from hue/saturation/value + * + * Hue expects a value between 0.0 and 360.0. Saturation and Value expect a value between 0.0 and 1.0. + */ + static Color fromHsv(const float& h, const float& s, const float &v); + Color(const char *data, const PixelFormat::Format &format); const float& red() const {return data_[0];} @@ -32,6 +39,11 @@ public: const float& blue() const {return data_[2];} const float& alpha() const {return data_[3];} + void toHsv(float* hue, float* sat, float* val) const; + float hue() const; + float saturation() const; + float value() const; + void set_red(const float& red) {data_[0] = red;} void set_green(const float& green) {data_[1] = green;} void set_blue(const float& blue) {data_[2] = blue;} diff --git a/app/shaders/colorgradient.frag b/app/shaders/colorgradient.frag new file mode 100644 index 000000000..c21d32fa6 --- /dev/null +++ b/app/shaders/colorgradient.frag @@ -0,0 +1,25 @@ +#version 110 + +// Standard inputs +uniform vec2 ove_resolution; +varying vec2 ove_texcoord; + +// Inputs +uniform int orientation; +uniform vec3 color_a; +uniform vec3 color_b; + +void main(void) { + float t; + + // This very roughly conforms to Qt::Orientation + if (orientation == 1) { + // Horizontal orientation + t = ove_texcoord.x; + } else { + // Vertical orientation + t = 1.0 - ove_texcoord.y; + } + + gl_FragColor = vec4(mix(color_a, color_b, t), 1.0); +} diff --git a/app/shaders/colorwheel.frag b/app/shaders/colorwheel.frag new file mode 100644 index 000000000..195b639e1 --- /dev/null +++ b/app/shaders/colorwheel.frag @@ -0,0 +1,91 @@ +#version 110 + +// Standard inputs +uniform vec2 ove_resolution; +varying vec2 ove_texcoord; + +// Custom inputs +uniform float hsv_value; + +// Color wheel uses PI +#define M_PI 3.1415926535897932384626433832795 + +vec3 hsv_to_rgb(float H, float S, float V) { + float C = S * V; + float X = C * (1.0 - abs(mod(H / 60.0, 2.0) - 1.0)); + float m = V - C; + float Rs, Gs, Bs; + + if(H >= 0.0 && H < 60.0) { + Rs = C; + Gs = X; + Bs = 0.0; + } + else if(H >= 60.0 && H < 120.0) { + Rs = X; + Gs = C; + Bs = 0.0; + } + else if(H >= 120.0 && H < 180.0) { + Rs = 0.0; + Gs = C; + Bs = X; + } + else if(H >= 180.0 && H < 240.0) { + Rs = 0.0; + Gs = X; + Bs = C; + } + else if(H >= 240.0 && H < 300.0) { + Rs = X; + Gs = 0.0; + Bs = C; + } + else { + Rs = C; + Gs = 0.0; + Bs = X; + } + + return vec3(Rs + m, Gs + m, Bs + m); +} + +void main(void) { + vec2 center = vec2(0.5, 0.5); + float radius = 0.5; + + vec2 flipped_texcoord = vec2(ove_texcoord.x, (1.0 - ove_texcoord.y)); + + // Calculate scale + if (ove_resolution.x != ove_resolution.y) { + // Scale around center + flipped_texcoord -= vec2(0.5, 0.5); + + if (ove_resolution.x > ove_resolution.y) { + flipped_texcoord.x *= ove_resolution.x / ove_resolution.y; + } else { + flipped_texcoord.y *= ove_resolution.y / ove_resolution.x; + } + + flipped_texcoord += vec2(0.5, 0.5); + } + + float dist = distance(flipped_texcoord, center); + + if (dist <= radius) { + float opposite = flipped_texcoord.y - center.y; + float adjacent = flipped_texcoord.x - center.x; + + float hue = atan(opposite, adjacent) * 180.0 / M_PI + 180.0; + float sat = dist / radius; + + // Extremely simple antialiasing, we taper off the edges between (radius) and (radius - 1) + float pixel_radius = min(ove_resolution.x, ove_resolution.y) * 0.5; + float pixel_dist = (pixel_radius / radius) * dist; + float alpha = min((pixel_radius - pixel_dist), 1.0); + + gl_FragColor = vec4(hsv_to_rgb(hue, sat, hsv_value), alpha); + } else { + gl_FragColor = vec4(0.0); + } +} diff --git a/app/shaders/shaders.qrc b/app/shaders/shaders.qrc index 19a9782f5..9715568ec 100644 --- a/app/shaders/shaders.qrc +++ b/app/shaders/shaders.qrc @@ -4,6 +4,8 @@ alphaover.xml boxblur.frag boxblur.xml + colorgradient.frag + colorwheel.frag crossdissolve.frag crossdissolve.xml dropshadow.frag diff --git a/app/widget/colorbutton/colorbutton.cpp b/app/widget/colorbutton/colorbutton.cpp index feb95afcb..086b312a7 100644 --- a/app/widget/colorbutton/colorbutton.cpp +++ b/app/widget/colorbutton/colorbutton.cpp @@ -5,11 +5,13 @@ ColorButton::ColorButton(QWidget *parent) : QPushButton(parent) { + color_ = Color(1.0f, 1.0f, 1.0f); + connect(this, &ColorButton::clicked, this, &ColorButton::ShowColorDialog); } void ColorButton::ShowColorDialog() { - ColorDialog cd(nullptr); + ColorDialog cd(nullptr, color_, this); cd.exec(); } diff --git a/app/widget/colorbutton/colorbutton.h b/app/widget/colorbutton/colorbutton.h index 132182b0a..d1a7422df 100644 --- a/app/widget/colorbutton/colorbutton.h +++ b/app/widget/colorbutton/colorbutton.h @@ -3,6 +3,8 @@ #include +#include "render/color.h" + class ColorButton : public QPushButton { Q_OBJECT @@ -15,6 +17,9 @@ signals: private slots: void ShowColorDialog(); +private: + Color color_; + }; #endif // COLORBUTTON_H diff --git a/app/widget/colorwheel/CMakeLists.txt b/app/widget/colorwheel/CMakeLists.txt index cfbafbbf5..6c9c3a784 100644 --- a/app/widget/colorwheel/CMakeLists.txt +++ b/app/widget/colorwheel/CMakeLists.txt @@ -16,6 +16,10 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} + widget/colorwheel/colorgradientwidget.h + widget/colorwheel/colorgradientwidget.cpp + widget/colorwheel/colorswatchwidget.h + widget/colorwheel/colorswatchwidget.cpp widget/colorwheel/colorwheelwidget.h widget/colorwheel/colorwheelwidget.cpp PARENT_SCOPE diff --git a/app/widget/colorwheel/colorgradientwidget.cpp b/app/widget/colorwheel/colorgradientwidget.cpp new file mode 100644 index 000000000..bd32a9dcd --- /dev/null +++ b/app/widget/colorwheel/colorgradientwidget.cpp @@ -0,0 +1,57 @@ +#include "colorgradientwidget.h" + +#include "common/lerp.h" +#include "node/node.h" + +ColorGradientGLWidget::ColorGradientGLWidget(Qt::Orientation orientation, QWidget *parent) : + ColorSwatchWidget(parent), + orientation_(orientation) +{ +} + +void ColorGradientGLWidget::SetColors(const Color &a, const Color &b) +{ + a_ = a; + b_ = b; + + update(); +} + +Color ColorGradientGLWidget::GetColorFromCursorPos(const QPoint &p) const +{ + float t; + + if (orientation_ == Qt::Horizontal) { + t = static_cast(p.x()) / static_cast(width()); + } else { + t = static_cast(p.y()) / static_cast(height()); + } + + return Color(lerp(a_.red(), b_.red(), t), + lerp(a_.green(), b_.green(), t), + lerp(a_.blue(), b_.blue(), t)); +} + +bool ColorGradientGLWidget::PointIsValid(const QPoint &) const +{ + return true; +} + +OpenGLShader *ColorGradientGLWidget::CreateShader() const +{ + OpenGLShader* s = new OpenGLShader(); + + s->create(); + s->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex()); + s->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(QStringLiteral(":/shaders/colorgradient.frag"))); + s->link(); + + return s; +} + +void ColorGradientGLWidget::SetShaderUniformValues(OpenGLShader *shader) const +{ + shader->setUniformValue("orientation", orientation_); + shader->setUniformValue("color_a", a_.red(), a_.green(), a_.blue()); + shader->setUniformValue("color_b", b_.red(), b_.green(), b_.blue()); +} diff --git a/app/widget/colorwheel/colorgradientwidget.h b/app/widget/colorwheel/colorgradientwidget.h new file mode 100644 index 000000000..7bb97f5f2 --- /dev/null +++ b/app/widget/colorwheel/colorgradientwidget.h @@ -0,0 +1,33 @@ +#ifndef COLORGRADIENTGLWIDGET_H +#define COLORGRADIENTGLWIDGET_H + +#include "colorswatchwidget.h" +#include "render/color.h" + +class ColorGradientGLWidget : public ColorSwatchWidget +{ + Q_OBJECT +public: + ColorGradientGLWidget(Qt::Orientation orientation, QWidget* parent = nullptr); + + void SetColors(const Color& a, const Color& b); + +protected: + virtual Color GetColorFromCursorPos(const QPoint& p) const override; + + virtual bool PointIsValid(const QPoint& p) const override; + + virtual OpenGLShader* CreateShader() const override; + + virtual void SetShaderUniformValues(OpenGLShader* shader) const override; + +private: + Qt::Orientation orientation_; + + Color a_; + + Color b_; + +}; + +#endif // COLORGRADIENTGLWIDGET_H diff --git a/app/widget/colorwheel/colorswatchwidget.cpp b/app/widget/colorwheel/colorswatchwidget.cpp new file mode 100644 index 000000000..8adfd00a5 --- /dev/null +++ b/app/widget/colorwheel/colorswatchwidget.cpp @@ -0,0 +1,61 @@ +#include "colorswatchwidget.h" + +#include + +#include "render/backend/opengl/openglrenderfunctions.h" + +ColorSwatchWidget::ColorSwatchWidget(QWidget *parent) : + QOpenGLWidget(parent), + shader_(nullptr) +{ + // Render with transparent background + setAttribute(Qt::WA_AlwaysStackOnTop); +} + +ColorSwatchWidget::~ColorSwatchWidget() +{ + CleanUp(); +} + +void ColorSwatchWidget::initializeGL() +{ + CleanUp(); + + shader_ = CreateShader(); + + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ColorSwatchWidget::CleanUp); +} + +void ColorSwatchWidget::paintGL() +{ + shader_->bind(); + shader_->setUniformValue("ove_resolution", width(), height()); + SetShaderUniformValues(shader_); + shader_->release(); + + OpenGLRenderFunctions::Blit(shader_); +} + +void ColorSwatchWidget::CleanUp() +{ + delete shader_; + shader_ = nullptr; +} + +void ColorSwatchWidget::mousePressEvent(QMouseEvent *e) +{ + QOpenGLWidget::mousePressEvent(e); + + if (PointIsValid(e->pos())) { + emit ColorChanged(GetColorFromCursorPos(e->pos())); + } +} + +void ColorSwatchWidget::mouseMoveEvent(QMouseEvent *e) +{ + QOpenGLWidget::mouseMoveEvent(e); + + if (e->buttons() & Qt::LeftButton && PointIsValid(e->pos())) { + emit ColorChanged(GetColorFromCursorPos(e->pos())); + } +} diff --git a/app/widget/colorwheel/colorswatchwidget.h b/app/widget/colorwheel/colorswatchwidget.h new file mode 100644 index 000000000..9aff04d6f --- /dev/null +++ b/app/widget/colorwheel/colorswatchwidget.h @@ -0,0 +1,45 @@ +#ifndef COLORSWATCHWIDGET_H +#define COLORSWATCHWIDGET_H + +#include + +#include "render/backend/opengl/openglshader.h" +#include "render/color.h" + +class ColorSwatchWidget : public QOpenGLWidget +{ + Q_OBJECT +public: + ColorSwatchWidget(QWidget* parent = nullptr); + + virtual ~ColorSwatchWidget() override; + +signals: + void ColorChanged(const Color& c); + +protected: + virtual void initializeGL() override; + + virtual void paintGL() override; + + virtual void mousePressEvent(QMouseEvent* e) override; + + virtual void mouseMoveEvent(QMouseEvent* e) override; + + virtual void SetShaderUniformValues(OpenGLShader*) const {} + + virtual Color GetColorFromCursorPos(const QPoint& p) const = 0; + + virtual bool PointIsValid(const QPoint& p) const = 0; + + virtual OpenGLShader* CreateShader() const = 0; + +private slots: + void CleanUp(); + +private: + OpenGLShader* shader_; + +}; + +#endif // COLORSWATCHWIDGET_H diff --git a/app/widget/colorwheel/colorwheelwidget.cpp b/app/widget/colorwheel/colorwheelwidget.cpp index 3ea153325..8fbb648a1 100644 --- a/app/widget/colorwheel/colorwheelwidget.cpp +++ b/app/widget/colorwheel/colorwheelwidget.cpp @@ -1,16 +1,81 @@ #include "colorwheelwidget.h" -#include #include -#define RADIAN_TO_0_1_RANGE 0.15915494309188486110703542313983 +#include "node/node.h" -ColorWheelWidget::ColorWheelWidget(QWidget *parent) : - QWidget(parent) +#define M_180_OVER_PI 57.295791433133264917914229473464 + +ColorWheelGLWidget::ColorWheelGLWidget(QWidget *parent) : + ColorSwatchWidget(parent), + hsv_value_(1.0f) { - } +void ColorWheelGLWidget::SetHSVValue(float val) +{ + hsv_value_ = val; + update(); +} + +Color ColorWheelGLWidget::GetColorFromCursorPos(const QPoint &p) const +{ + QPoint center = rect().center(); + + qreal opposite = p.y() - center.y(); + qreal adjacent = p.x() - center.x(); + qreal distance = qSqrt(qPow(adjacent, 2) + qPow(opposite, 2)); + + qreal hue = qAtan2(opposite, adjacent) * M_180_OVER_PI + 180.0; + qreal sat = (distance / GetRadius()); + qreal value = hsv_value_; + + return Color::fromHsv(hue, sat, value); +} + +bool ColorWheelGLWidget::PointIsValid(const QPoint &p) const +{ + QPoint center = rect().center(); + qreal distance = qSqrt(qPow(p.x() - center.x(), 2) + qPow(p.y() - center.y(), 2)); + + return (distance <= GetRadius()); +} + +OpenGLShader *ColorWheelGLWidget::CreateShader() const +{ + OpenGLShader* s = new OpenGLShader(); + + s->create(); + s->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex()); + s->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(QStringLiteral(":/shaders/colorwheel.frag"))); + s->link(); + + return s; +} + +void ColorWheelGLWidget::SetShaderUniformValues(OpenGLShader *shader) const +{ + shader->setUniformValue("hsv_value", hsv_value_); +} + +void ColorWheelGLWidget::resizeEvent(QResizeEvent *e) +{ + ColorSwatchWidget::resizeEvent(e); + + emit DiameterChanged(GetDiameter()); +} + +int ColorWheelGLWidget::GetDiameter() const +{ + return qMin(width(), height()); +} + +qreal ColorWheelGLWidget::GetRadius() const +{ + return GetDiameter() * 0.5; +} + +/* Software/QPainter-based color wheel void ColorWheelWidget::paintEvent(QPaintEvent *event) { QWidget::paintEvent(event); @@ -20,12 +85,10 @@ void ColorWheelWidget::paintEvent(QPaintEvent *event) qreal radius = qMin(width(), height()) / 2; QPoint center = rect().center(); - p.setPen(Qt::red); - int x_start = center.x() - radius; - int x_end= center.x() + radius; + int x_end = center.x() + radius; int y_start = center.y() - radius; - int y_end= center.y() + radius; + int y_end = center.y() + radius; for (int i=x_start;i +#include -class ColorWheelWidget : public QWidget +#include "colorswatchwidget.h" +#include "render/backend/opengl/openglshader.h" +#include "render/color.h" + +class ColorWheelGLWidget : public ColorSwatchWidget +{ + Q_OBJECT +public: + ColorWheelGLWidget(QWidget* parent = nullptr); + + void SetHSVValue(float val); + +signals: + void DiameterChanged(int radius); + +protected: + virtual Color GetColorFromCursorPos(const QPoint& p) const override; + + virtual bool PointIsValid(const QPoint& p) const override; + + virtual OpenGLShader* CreateShader() const override; + + virtual void SetShaderUniformValues(OpenGLShader* shader) const override; + + virtual void resizeEvent(QResizeEvent* e) override; + +private: + int GetDiameter() const; + + qreal GetRadius() const; + + float hsv_value_; + +}; + +/*class ColorWheelWidget : public QWidget { public: ColorWheelWidget(QWidget* parent = nullptr); protected: - virtual void paintEvent(QPaintEvent* event) override; + virtual void paintEvent(QPaintEvent* e) override; -}; +};*/ #endif // COLORWHEELWIDGET_H