diff --git a/app/common/define.h b/app/common/define.h index 2bed051f9..5870b2dc1 100644 --- a/app/common/define.h +++ b/app/common/define.h @@ -21,6 +21,7 @@ #ifndef OLIVECOMMONDEFINE_H #define OLIVECOMMONDEFINE_H +const int kHSVChannels = 3; const int kRGBChannels = 3; const int kRGBAChannels = 4; diff --git a/app/dialog/color/colordialog.cpp b/app/dialog/color/colordialog.cpp index be8cf1e3c..277272ee7 100644 --- a/app/dialog/color/colordialog.cpp +++ b/app/dialog/color/colordialog.cpp @@ -15,60 +15,43 @@ ColorDialog::ColorDialog(ColorManager* color_manager, Color start, QWidget *pare QVBoxLayout* layout = new QVBoxLayout(this); QSplitter* splitter = new QSplitter(Qt::Horizontal); + splitter->setChildrenCollapsible(false); layout->addWidget(splitter); QWidget* wheel_area = new QWidget(); QHBoxLayout* wheel_layout = new QHBoxLayout(wheel_area); splitter->addWidget(wheel_area); - color_wheel_ = new ColorWheelGLWidget(); + color_wheel_ = new ColorWheelWidget(); wheel_layout->addWidget(color_wheel_); - hsv_value_gradient_ = new ColorGradientGLWidget(Qt::Vertical); + hsv_value_gradient_ = new ColorGradientWidget(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); + splitter->setSizes({INT_MAX, 0}); + + connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, cvw, &ColorValuesWidget::SetColor); + connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor); + connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, cvw, &ColorValuesWidget::SetColor); + connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor); + + connect(color_wheel_, &ColorWheelWidget::DiameterChanged, hsv_value_gradient_, &ColorGradientWidget::setFixedHeight); 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); + color_wheel_->SetSelectedColor(start); + hsv_value_gradient_->SetSelectedColor(start); cvw->SetColor(start); } -void ColorDialog::UpdateValueGradient(const Color &c) +const Color &ColorDialog::GetSelectedColor() const { - 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); + return color_wheel_->GetSelectedColor(); } diff --git a/app/dialog/color/colordialog.h b/app/dialog/color/colordialog.h index 06dfc1a22..6669e8835 100644 --- a/app/dialog/color/colordialog.h +++ b/app/dialog/color/colordialog.h @@ -15,19 +15,14 @@ class ColorDialog : public QDialog public: ColorDialog(ColorManager* color_manager, Color start = Color(1.0f, 1.0f, 1.0f), QWidget* parent = nullptr); + const Color& GetSelectedColor() const; + private: ColorManager* color_manager_; - ColorWheelGLWidget* color_wheel_; + ColorWheelWidget* color_wheel_; - ColorGradientGLWidget* hsv_value_gradient_; - -private slots: - void UpdateValueGradient(const Color& c); - - void UpdateWheelValue(const Color& c); - - void UpdateValueGradientSize(int diameter); + ColorGradientWidget* hsv_value_gradient_; }; diff --git a/app/render/color.cpp b/app/render/color.cpp index 225f30c5f..c9a79a4f3 100644 --- a/app/render/color.cpp +++ b/app/render/color.cpp @@ -87,14 +87,14 @@ void Color::toHsv(float *hue, float *sat, float *val) const } } -float Color::hue() const +float Color::hsv_hue() const { float h, s, v; toHsv(&h, &s, &v); return h; } -float Color::saturation() const +float Color::hsv_saturation() const { float h, s, v; toHsv(&h, &s, &v); @@ -107,3 +107,77 @@ float Color::value() const toHsv(&h, &s, &v); return v; } + +void Color::toHsl(float *hue, float *sat, float *lightness) const +{ + float fCMin = qMin(red(), qMin(green(), blue())); + float fCMax = qMax(red(), qMax(green(), blue())); + + *lightness = 0.5 * (fCMin + fCMax); + + if (fCMin == fCMax) + { + *sat = 0; + *hue = 0; + return; + + } + else if (*lightness < 0.5) + { + *sat = (fCMax - fCMin) / (fCMax + fCMin); + } + else + { + *sat = (fCMax - fCMin) / (2.0 - fCMax - fCMin); + } + + if (fCMax == red()) + { + *hue = 60 * (green() - blue()) / (fCMax - fCMin); + } + if (fCMax == green()) + { + *hue = 60 * (blue() - red()) / (fCMax - fCMin) + 120; + } + if (fCMax == blue()) + { + *hue = 60 * (red() - green()) / (fCMax - fCMin) + 240; + } + if (*hue < 0) + { + *hue = *hue + 360; + } +} + +float Color::hsl_hue() const +{ + float h, s, l; + toHsl(&h, &s, &l); + return h; +} + +float Color::hsl_saturation() const +{ + float h, s, l; + toHsl(&h, &s, &l); + return s; +} + +float Color::lightness() const +{ + float h, s, l; + toHsl(&h, &s, &l); + return l; +} + +QColor Color::toQColor() const +{ + QColor c; + + c.setRedF(red()); + c.setGreenF(green()); + c.setBlueF(blue()); + c.setAlphaF(alpha()); + + return c; +} diff --git a/app/render/color.h b/app/render/color.h index abc35071d..03c160abd 100644 --- a/app/render/color.h +++ b/app/render/color.h @@ -1,6 +1,8 @@ #ifndef COLOR_H #define COLOR_H +#include + #include "common/define.h" #include "render/pixelformat.h" @@ -40,10 +42,15 @@ public: const float& alpha() const {return data_[3];} void toHsv(float* hue, float* sat, float* val) const; - float hue() const; - float saturation() const; + float hsv_hue() const; + float hsv_saturation() const; float value() const; + void toHsl(float* hue, float* sat, float* lightness) const; + float hsl_hue() const; + float hsl_saturation() const; + float lightness() 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;} @@ -54,6 +61,8 @@ public: static Color fromData(const char* data, const PixelFormat::Format& format); + QColor toQColor() const; + private: float data_[kRGBAChannels]; diff --git a/app/widget/colorbutton/colorbutton.cpp b/app/widget/colorbutton/colorbutton.cpp index 086b312a7..a602b9d74 100644 --- a/app/widget/colorbutton/colorbutton.cpp +++ b/app/widget/colorbutton/colorbutton.cpp @@ -8,10 +8,21 @@ ColorButton::ColorButton(QWidget *parent) : color_ = Color(1.0f, 1.0f, 1.0f); connect(this, &ColorButton::clicked, this, &ColorButton::ShowColorDialog); + + UpdateColor(); } void ColorButton::ShowColorDialog() { ColorDialog cd(nullptr, color_, this); - cd.exec(); + + if (cd.exec() == QDialog::Accepted) { + color_ = cd.GetSelectedColor(); + UpdateColor(); + } +} + +void ColorButton::UpdateColor() +{ + setStyleSheet(QStringLiteral("ColorButton {background: %1;}").arg(color_.toQColor().name())); } diff --git a/app/widget/colorbutton/colorbutton.h b/app/widget/colorbutton/colorbutton.h index d1a7422df..3d82d1099 100644 --- a/app/widget/colorbutton/colorbutton.h +++ b/app/widget/colorbutton/colorbutton.h @@ -18,6 +18,8 @@ private slots: void ShowColorDialog(); private: + void UpdateColor(); + Color color_; }; diff --git a/app/widget/colorwheel/colorgradientwidget.cpp b/app/widget/colorwheel/colorgradientwidget.cpp index bd32a9dcd..6199003f2 100644 --- a/app/widget/colorwheel/colorgradientwidget.cpp +++ b/app/widget/colorwheel/colorgradientwidget.cpp @@ -1,57 +1,82 @@ #include "colorgradientwidget.h" +#include + +#include "common/clamp.h" #include "common/lerp.h" #include "node/node.h" -ColorGradientGLWidget::ColorGradientGLWidget(Qt::Orientation orientation, QWidget *parent) : +ColorGradientWidget::ColorGradientWidget(Qt::Orientation orientation, QWidget *parent) : ColorSwatchWidget(parent), - orientation_(orientation) + orientation_(orientation), + val_(1.0) { } -void ColorGradientGLWidget::SetColors(const Color &a, const Color &b) +Color ColorGradientWidget::GetColorFromScreenPos(const QPoint &p) const { - a_ = a; - b_ = b; - - update(); + if (orientation_ == Qt::Horizontal) { + return LerpColor(start_, end_, p.x(), width()); + } else { + return LerpColor(start_, end_, p.y(), height()); + } } -Color ColorGradientGLWidget::GetColorFromCursorPos(const QPoint &p) const +void ColorGradientWidget::paintEvent(QPaintEvent *e) { - float t; + QWidget::paintEvent(e); + + QPainter p(this); + + int min; + int max; if (orientation_ == Qt::Horizontal) { - t = static_cast(p.x()) / static_cast(width()); + min = height(); + max = width(); } else { - t = static_cast(p.y()) / static_cast(height()); + min = width(); + max = height(); } - return Color(lerp(a_.red(), b_.red(), t), - lerp(a_.green(), b_.green(), t), - lerp(a_.blue(), b_.blue(), t)); + for (int i=0;i(i) / static_cast(max), 0.0f, 1.0f); - 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()); + return Color(lerp(a.red(), b.red(), t), + lerp(a.green(), b.green(), t), + lerp(a.blue(), b.blue(), t)); } diff --git a/app/widget/colorwheel/colorgradientwidget.h b/app/widget/colorwheel/colorgradientwidget.h index 7bb97f5f2..afcb358c3 100644 --- a/app/widget/colorwheel/colorgradientwidget.h +++ b/app/widget/colorwheel/colorgradientwidget.h @@ -4,29 +4,29 @@ #include "colorswatchwidget.h" #include "render/color.h" -class ColorGradientGLWidget : public ColorSwatchWidget +class ColorGradientWidget : public ColorSwatchWidget { Q_OBJECT public: - ColorGradientGLWidget(Qt::Orientation orientation, QWidget* parent = nullptr); - - void SetColors(const Color& a, const Color& b); + ColorGradientWidget(Qt::Orientation orientation, QWidget* parent = nullptr); protected: - virtual Color GetColorFromCursorPos(const QPoint& p) const override; + virtual Color GetColorFromScreenPos(const QPoint& p) const override; - virtual bool PointIsValid(const QPoint& p) const override; + virtual void paintEvent(QPaintEvent* e) override; - virtual OpenGLShader* CreateShader() const override; - - virtual void SetShaderUniformValues(OpenGLShader* shader) const override; + virtual void SelectedColorChangedEvent(const Color& c) override; private: + static Color LerpColor(const Color& a, const Color& b, int i, int max); + Qt::Orientation orientation_; - Color a_; + Color start_; - Color b_; + Color end_; + + float val_; }; diff --git a/app/widget/colorwheel/colorswatchwidget.cpp b/app/widget/colorwheel/colorswatchwidget.cpp index 8adfd00a5..514a6e49a 100644 --- a/app/widget/colorwheel/colorswatchwidget.cpp +++ b/app/widget/colorwheel/colorswatchwidget.cpp @@ -5,57 +5,51 @@ #include "render/backend/opengl/openglrenderfunctions.h" ColorSwatchWidget::ColorSwatchWidget(QWidget *parent) : - QOpenGLWidget(parent), - shader_(nullptr) + QWidget(parent) { - // Render with transparent background - setAttribute(Qt::WA_AlwaysStackOnTop); } -ColorSwatchWidget::~ColorSwatchWidget() +const Color &ColorSwatchWidget::GetSelectedColor() const { - CleanUp(); + return selected_color_; } -void ColorSwatchWidget::initializeGL() +void ColorSwatchWidget::SetSelectedColor(const Color &c) { - 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; + selected_color_ = c; + SelectedColorChangedEvent(c); + update(); } void ColorSwatchWidget::mousePressEvent(QMouseEvent *e) { - QOpenGLWidget::mousePressEvent(e); + QWidget::mousePressEvent(e); - if (PointIsValid(e->pos())) { - emit ColorChanged(GetColorFromCursorPos(e->pos())); - } + SetSelectedColor(GetColorFromScreenPos(e->pos())); + emit SelectedColorChanged(GetSelectedColor()); } void ColorSwatchWidget::mouseMoveEvent(QMouseEvent *e) { - QOpenGLWidget::mouseMoveEvent(e); + QWidget::mouseMoveEvent(e); - if (e->buttons() & Qt::LeftButton && PointIsValid(e->pos())) { - emit ColorChanged(GetColorFromCursorPos(e->pos())); + if (e->buttons() & Qt::LeftButton) { + SetSelectedColor(GetColorFromScreenPos(e->pos())); + emit SelectedColorChanged(GetSelectedColor()); + } +} + +void ColorSwatchWidget::SelectedColorChangedEvent(const Color &) +{ +} + +Qt::GlobalColor ColorSwatchWidget::GetUISelectorColor() const +{ + float rough_color_luma = (GetSelectedColor().red()+GetSelectedColor().red()+GetSelectedColor().blue()+GetSelectedColor().green()+GetSelectedColor().green()+GetSelectedColor().green())/6; + + if (rough_color_luma > 0.66) { + return Qt::black; + } else { + return Qt::white; } } diff --git a/app/widget/colorwheel/colorswatchwidget.h b/app/widget/colorwheel/colorswatchwidget.h index 9aff04d6f..eff2ba8ec 100644 --- a/app/widget/colorwheel/colorswatchwidget.h +++ b/app/widget/colorwheel/colorswatchwidget.h @@ -6,39 +6,33 @@ #include "render/backend/opengl/openglshader.h" #include "render/color.h" -class ColorSwatchWidget : public QOpenGLWidget +class ColorSwatchWidget : public QWidget { Q_OBJECT public: ColorSwatchWidget(QWidget* parent = nullptr); - virtual ~ColorSwatchWidget() override; + const Color& GetSelectedColor() const; + +public slots: + void SetSelectedColor(const Color& c); signals: - void ColorChanged(const Color& c); + void SelectedColorChanged(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 GetColorFromScreenPos(const QPoint& p) const = 0; - virtual Color GetColorFromCursorPos(const QPoint& p) const = 0; + virtual void SelectedColorChangedEvent(const Color& c); - virtual bool PointIsValid(const QPoint& p) const = 0; - - virtual OpenGLShader* CreateShader() const = 0; - -private slots: - void CleanUp(); + Qt::GlobalColor GetUISelectorColor() const; private: - OpenGLShader* shader_; + Color selected_color_; }; diff --git a/app/widget/colorwheel/colorwheelwidget.cpp b/app/widget/colorwheel/colorwheelwidget.cpp index 8fbb648a1..fbd64831b 100644 --- a/app/widget/colorwheel/colorwheelwidget.cpp +++ b/app/widget/colorwheel/colorwheelwidget.cpp @@ -1,112 +1,152 @@ #include "colorwheelwidget.h" +#include #include #include "node/node.h" #define M_180_OVER_PI 57.295791433133264917914229473464 +#define M_RADIAN_TO_0_1 0.15915497620314795810531730409296 -ColorWheelGLWidget::ColorWheelGLWidget(QWidget *parent) : +ColorWheelWidget::ColorWheelWidget(QWidget *parent) : ColorSwatchWidget(parent), - hsv_value_(1.0f) + val_(1.0f), + force_redraw_(false) { } -void ColorWheelGLWidget::SetHSVValue(float val) +Color ColorWheelWidget::GetColorFromScreenPos(const QPoint &p) const { - hsv_value_ = val; - update(); + return GetColorFromTriangle(GetTriangleFromCoords(rect().center(), p)); } -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) +void ColorWheelWidget::resizeEvent(QResizeEvent *e) { ColorSwatchWidget::resizeEvent(e); emit DiameterChanged(GetDiameter()); } -int ColorWheelGLWidget::GetDiameter() const +void ColorWheelWidget::paintEvent(QPaintEvent *e) +{ + ColorSwatchWidget::paintEvent(e); + + int diameter = GetDiameter(); + + // Half diameter (add one to ensure the division rounds up) + int radius = (diameter + 1) / 2; + + if (cached_wheel_.width() != diameter || force_redraw_) { + cached_wheel_ = QPixmap(QSize(diameter, diameter)); + cached_wheel_.fill(Qt::transparent); + force_redraw_ = false; + + QPainter p(&cached_wheel_); + QPoint center(radius, radius); + + for (int i=0;i height()) { + x = (width() - height()) / 2; + y = 0; + } else { + x = 0; + y = (height() - width()) / 2; + } + + p.drawPixmap(x, y, cached_wheel_); + + + // Draw selection + // Really rough algorithm for determining whether the selector UI should be white or black + + + int selector_radius = qMax(1, radius / 32); + p.setPen(QPen(GetUISelectorColor(), qMax(1, selector_radius / 4))); + p.setBrush(Qt::NoBrush); + + p.drawEllipse(GetCoordsFromColor(GetSelectedColor()), selector_radius, selector_radius); +} + +void ColorWheelWidget::SelectedColorChangedEvent(const Color &c) +{ + force_redraw_ = true; + val_ = c.value(); +} + +int ColorWheelWidget::GetDiameter() const { return qMin(width(), height()); } -qreal ColorWheelGLWidget::GetRadius() const +qreal ColorWheelWidget::GetRadius() const { return GetDiameter() * 0.5; } -/* Software/QPainter-based color wheel -void ColorWheelWidget::paintEvent(QPaintEvent *event) +ColorWheelWidget::Triangle ColorWheelWidget::GetTriangleFromCoords(const QPoint ¢er, const QPoint &p) const { - QWidget::paintEvent(event); + return GetTriangleFromCoords(center, p.y(), p.x()); +} - QPainter p(this); +ColorWheelWidget::Triangle ColorWheelWidget::GetTriangleFromCoords(const QPoint ¢er, qreal y, qreal x) const +{ + qreal opposite = y - center.y(); + qreal adjacent = x - center.x(); + qreal hypotenuse = qSqrt(qPow(adjacent, 2) + qPow(opposite, 2)); - qreal radius = qMin(width(), height()) / 2; - QPoint center = rect().center(); + return {opposite, adjacent, hypotenuse}; +} - int x_start = center.x() - radius; - int x_end = center.x() + radius; - int y_start = center.y() - radius; - int y_end = center.y() + radius; +Color ColorWheelWidget::GetColorFromTriangle(const ColorWheelWidget::Triangle &tri) const +{ + qreal hue = qAtan2(tri.opposite, tri.adjacent) * M_180_OVER_PI + 180.0; + qreal sat = qMin(1.0, (tri.hypotenuse / GetRadius())); - for (int i=x_start;i