Nicolas Winkler 5 лет назад
Родитель
Сommit
1f400ab2bc
8 измененных файлов с 110 добавлено и 233 удалено
  1. 27 0
      Almond.cpp
  2. 0 164
      Gradient.cpp
  3. 0 38
      Gradient.h
  4. 11 4
      GradientMenu.cpp
  5. 5 1
      GradientMenu.h
  6. 57 18
      GradientWidget.cpp
  7. 9 7
      GradientWidget.h
  8. 1 1
      gradientchoosedialog.cpp

+ 27 - 0
Almond.cpp

@@ -193,6 +193,33 @@ void Almond::videoExportOk(void)
 
 void Almond::gradientEditOk(void)
 {
+    const auto& points = gradientMenu->getGradient();
+
+    // convert from QVector<QPair<float, QColor>> to
+    //           -> std::vector<std::pair<RGBColor, float>>
+    std::vector<std::pair<RGBColor, float>> np;
+    std::transform(points.begin(), points.end(), std::back_inserter(np),
+        [](auto& qp) -> std::pair<RGBColor, float> {
+        auto& [pos, col] = qp;
+        return { RGBColor{ uint8_t(col.red()), uint8_t(col.green()), uint8_t(col.blue()) },
+            pos };
+    });
+    std::sort(np.begin(), np.end(), [](auto& a, auto& b) { return a.second > b.second; });
+    if (!np.empty()) {
+        auto& first = np.at(0);
+        if (first.second > 0) {
+            np.insert(np.begin(), { first.first, 0.0f });
+        }
+        auto& last = np.at(np.size() - 1);
+        if (last.second < 1) {
+            np.insert(np.begin(), { last.first, 1.0f });
+        }
+    }
+
+    std::for_each(np.begin(), np.end(), [](auto& x) { x.second *= 100; });
+
+    Gradient g{ np, true };
+    mw->setGradient(std::move(g));
     amw->showMainMenu();
 }
 

+ 0 - 164
Gradient.cpp

@@ -1,164 +0,0 @@
-#include "Gradient.h"
-
-#include "CubicSpline.h"
-
-#include <cmath>
-#include <algorithm>
-#include <functional>
-#include <QtXml/QDomDocument>
-#include <QFile>
-
-
-Gradient::Gradient(void) :
-    max{ 1.0 }
-{
-}
-
-
-Gradient::Gradient(std::vector<std::pair<RGBColor, float>> colors, bool repeat, int precalcSteps) :
-    repeat{ repeat }
-{
-    if(colors.empty() || colors.size() < 2)
-        return;
-    std::sort(colors.begin(), colors.end(),
-        [] (const auto& a, const auto& b) {
-            return a.second < b.second;
-        });
-
-    max = colors.at(colors.size() - 1).second;
-
-    std::vector<std::pair<RGBColorf, float>> linearColors;
-    std::transform(colors.begin(), colors.end(), std::back_inserter(linearColors),
-                   [] (auto c) { return c; });
-
-    std::vector<std::pair<float, float>> rs;
-    std::vector<std::pair<float, float>> gs;
-    std::vector<std::pair<float, float>> bs;
-
-    std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(rs),
-                   [] (auto p) { return std::pair{ p.second, p.first.r }; });
-    std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(gs),
-                   [] (auto p) { return std::pair{ p.second, p.first.g }; });
-    std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(bs),
-                   [] (auto p) { return std::pair{ p.second, p.first.b }; });
-
-    CubicSpline rsp(rs, false);
-    CubicSpline gsp(gs, false);
-    CubicSpline bsp(bs, false);
-
-    if(precalcSteps <= 0) {
-        precalcSteps = int(max * 15) + 10;
-    }
-
-    for (int i = 0; i < precalcSteps; i++) {
-        float position = i * max / precalcSteps;
-        RGBColorf at = {
-            rsp.interpolateAt(position),
-            gsp.interpolateAt(position),
-            bsp.interpolateAt(position)
-        };
-        this->colors.push_back(at);
-    }
-}
-
-
-Gradient Gradient::defaultGradient(void)
-{
-    QFile res(":/gradients/default");
-    res.open(QIODevice::ReadOnly);
-    QString str = QString::fromUtf8(res.readAll());
-    return readXml(str);
-}
-
-Gradient Gradient::readXml(const QString& xml)
-{
-    QDomDocument xsr;
-    xsr.setContent(xml);
-    auto elem = xsr.documentElement();
-    auto repeatAttr = elem.attributeNode("repeat");
-    bool repeat = !repeatAttr.isNull() && repeatAttr.value().toLower() == "true";
-    auto colors = xsr.elementsByTagName("color");
-    std::vector<std::pair<RGBColor, float>> colorArr;
-    for (int i = 0; i < colors.length(); ++i) {
-        auto child = colors.item(i).toElement();
-        uint8_t r = uint8_t(child.attributeNode("r").value().toInt());
-        uint8_t g = uint8_t(child.attributeNode("g").value().toInt());
-        uint8_t b = uint8_t(child.attributeNode("b").value().toInt());
-        float p = child.attributeNode("p").value().toInt();
-
-        //printf("rgb (%s): %d, %d, %d\n", child.text().toUtf8().data(), r, g, b);
-        colorArr.push_back({ { r, g, b }, p });
-    }
-
-    return Gradient(std::move(colorArr), repeat);
-}
-
-
-RGBColor Gradient::get(float x) const
-{
-    if (colors.empty() || std::isnan(x) || std::isinf(x))
-        return RGBColor();
-    /*const auto [left, right, lerp] = getNeighbors(x);
-    RGBColor lerped = lerpColors(left, right, lerp);
-    return lerped;*/
-
-    if (x < 0)
-        return colors[0];
-    if (x > this->max) {
-        if (repeat)
-            x = ::fmodf(x, this->max);
-        else
-            x = this->max;
-    }
-    float pos = x * colors.size() / max;
-    if (pos < 0) {
-        pos = 0;
-    }
-    if (pos > colors.size() - 1) {
-        pos = colors.size() - 1;
-    }
-
-    int left = int(pos);
-    int right = int(pos + 1);
-    float lerp = pos - left;
-
-    if (lerp < 1e-5f) {
-        return colors[left];
-    }
-    else {
-        return lerpColors(colors[left], colors[right], lerp);
-    }
-}
-
-
-RGBColorf Gradient::lerpColors(RGBColorf a, RGBColorf b, float val)
-{
-    return RGBColorf {
-        b.r * val + a.r * (1 - val),
-        b.g * val + a.g * (1 - val),
-        b.b * val + a.b * (1 - val)
-    };
-}
-
-
-RGBColor Gradient::lerpColors(RGBColor a, RGBColor b, float val)
-{
-    return RGBColor{ lerpColors(RGBColorf{ a }, RGBColorf{ b }, val) };
-}
-
-
-/*std::tuple<RGBColor, RGBColor, float> Gradient::getNeighbors(float x) const
-{
-    for (auto it = colors.begin(); it != colors.end(); ++it) {
-        if (it->second > x) {
-            if (it == colors.begin()) {
-                return { it->first, it->first, 0 };
-            }
-            else {
-                float lerp = (x - (it - 1)->second) / (it->second - (it - 1)->second);
-                return { (it - 1)->first, it->first, lerp };
-            }
-        }
-    }
-    return { (colors.end() - 1)->first, (colors.end() - 1)->first, 0 };
-}*/

+ 0 - 38
Gradient.h

@@ -1,38 +0,0 @@
-#ifndef GRADIENT_H
-#define GRADIENT_H
-
-#include <QString>
-#include <vector>
-#include "Color.h"
-#include <tuple>
-#include <cinttypes>
-
-
-class Gradient
-{
-    /// the colors of this gradient stored in linear RGB format
-    /// so they can be easily interpolated
-    std::vector<RGBColorf> colors;
-    float max;
-    bool repeat;
-public:
-    Gradient(void);
-    Gradient(std::vector<std::pair<RGBColor, float>> colors, bool repeat = false, int precalcSteps = -1);
-
-    static Gradient defaultGradient(void);
-
-    static Gradient readXml(const QString& xml);
-
-    /*!
-     * \brief get a color at a specific position in this gradient
-     * \param x the position
-     * \return the color in sRGB format
-     */
-    RGBColor get(float x) const;
-private:
-    static RGBColorf lerpColors(RGBColorf a, RGBColorf b, float val);
-    static RGBColor lerpColors(RGBColor a, RGBColor b, float val);
-    std::tuple<RGBColor, RGBColor, float> getNeighbors(float x) const;
-};
-
-#endif // GRADIENT_H

+ 11 - 4
GradientMenu.cpp

@@ -7,14 +7,21 @@ GradientMenu::GradientMenu(QWidget *parent) :
 {
     ui->setupUi(this);
     ui->gradientWidget->setGradient(
-    {
-                    {0.1, QColor{10, 200, 20}},
-                    {0.7, QColor{100, 20, 120}}
-    }
+        {
+            { 0.1, QColor{ 10, 200, 20 } },
+            { 0.7, QColor{ 100, 20, 120 } }
+        }
     );
 }
 
+
 GradientMenu::~GradientMenu()
 {
     delete ui;
 }
+
+
+const QVector<QPair<float, QColor>>& GradientMenu::getGradient(void)
+{
+    return ui->gradientWidget->getGradient();
+}

+ 5 - 1
GradientMenu.h

@@ -2,6 +2,8 @@
 #define GRADIENTMENU_H
 
 #include <QWidget>
+#include <QVector>
+#include <QPair>
 
 namespace Ui {
 class GradientMenu;
@@ -13,7 +15,9 @@ class GradientMenu : public QWidget
 
 public:
     explicit GradientMenu(QWidget *parent = nullptr);
-    ~GradientMenu();
+    ~GradientMenu(void);
+
+    const QVector<QPair<float, QColor>>& getGradient(void);
 
 private:
     Ui::GradientMenu *ui;

+ 57 - 18
GradientWidget.cpp

@@ -20,14 +20,13 @@ GradientWidget::GradientWidget(QWidget* parent) :
 }
 
 
-const QVector<std::pair<float, QColor>>&
-GradientWidget::getGradient(void) const
+const QVector<QPair<float, QColor>>& GradientWidget::getGradient(void) const
 {
     return points;
 }
 
 
-void GradientWidget::setGradient(QVector<std::pair<float, QColor>> vec)
+void GradientWidget::setGradient(QVector<QPair<float, QColor>> vec)
 {
     points = std::move(vec);
 }
@@ -64,18 +63,36 @@ QColor lerp(const QColor& a, const QColor& b, float v)
     return QColor{ int(255 * nr), int(255 * ng), int(255 * nb) };
 }
 
+
+QColor GradientWidget::colorAtY(float y)
+{
+    float v = handleYToGradVal(y);
+    QColor up = QColor(QColor::Invalid);
+    QColor down = QColor(QColor::Invalid);
+    float upv = 0;
+    float downv = 1;
+    for (const auto& [val, color] : points) {
+        if (val >= upv && val < v) {
+            upv = val;
+            up = color;
+        }
+        if (val <= downv && val > v) {
+            downv = val;
+            down = color;
+        }
+    }
+
+    if (!up.isValid())
+        return down;
+    if (!down.isValid())
+        return up;
+    return lerp(up, down, (v - upv) / (downv - upv));
+}
+
 void GradientWidget::paintEvent(QPaintEvent* e)
 {
     QPainter painter{ this };
 
-    std::vector<int> orderedIndices(points.size());
-    for (int i = 0; i < points.size(); i++)
-        orderedIndices.push_back(i);
-
-    std::sort(orderedIndices.begin(), orderedIndices.end(),
-        [this] (int l, int r) {
-            return points[l].first < points[r].first;
-        });
 
     QRect gradientRect = getGradientRect();
     QStyleOption frameOptions;
@@ -100,12 +117,24 @@ void GradientWidget::paintEvent(QPaintEvent* e)
     gradientRect.adjust(fhmargins, fvmargins, -fhmargins, -fvmargins);
     float lastPoint = 0;
     QColor lastColor = QColor{ 0, 0, 0 };
+
+
+
+    std::vector<int> orderedIndices(points.size());
+    for (int i = 0; i < points.size(); i++)
+        orderedIndices.push_back(i);
+
+    std::sort(orderedIndices.begin(), orderedIndices.end(),
+        [this] (int l, int r) {
+        return points[l].first < points[r].first;
+    });
+
     // traverse gradient in order and interpolate in linear
     // RGB space to avoid interpolating in sRGB
     for (int i = 0; i < orderedIndices.size(); i++) {
         int index = orderedIndices[i];
         auto& [point, color] = points[index];
-        int m = 5;
+        int m = 17;
         if (i > 0) {
             for (int i = 0; i < m; i++) {
                 float v = float(i) / m;
@@ -117,26 +146,29 @@ void GradientWidget::paintEvent(QPaintEvent* e)
         lastPoint = point;
         lastColor = color;
     }
+
     QBrush brush{ gradient };
     painter.fillRect(gradientRect, brush);
 
-    QStyleOption so;
-    so.init(this);
     int index = 0;
     for (auto& [point, color] : points) {
         QRect r = getHandleRect(index);
+        QStyleOptionButton so;
+        so.init(this);
         so.rect = r;
         if (dragging && selectedHandle == index)
             so.state |= QStyle::State_Sunken;
+        else if (selectedHandle == index)
+            so.state |= QStyle::State_HasFocus;
         else
-            so.state &= ~QStyle::State_Sunken;
+            so.state &= ~QStyle::State_Sunken & ~QStyle::State_HasFocus;
         if (mouseOver == index)
             so.state |= QStyle::State_MouseOver;
         else
             so.state &= ~QStyle::State_MouseOver;
+        
         so.palette.setColor(QPalette::ColorRole::Button, color);
-        so.palette.setColor(QPalette::ColorRole::Background, color);
-        style()->drawPrimitive(QStyle::PrimitiveElement::PE_PanelButtonTool, &so, &painter, this);
+        style()->drawControl(QStyle::ControlElement::CE_PushButton, &so, &painter, this);
         index++;
     }
     /*for (auto&[point, color] : points) {
@@ -188,7 +220,6 @@ void GradientWidget::mouseReleaseEvent(QMouseEvent* e)
 {
     if (dragging) {
         dragging = false;
-        selectedHandle = -1;
         update();
         e->accept();
     }
@@ -222,6 +253,7 @@ void GradientWidget::mouseMoveEvent(QMouseEvent* e)
 
 void GradientWidget::mouseDoubleClickEvent(QMouseEvent* e)
 {
+    QRect handleArea = getHandleArea();
     int handle = handleAtPos(e->pos());
     if (handle != -1) {
         QColor current = points.at(handle).second;
@@ -232,6 +264,13 @@ void GradientWidget::mouseDoubleClickEvent(QMouseEvent* e)
             points[handle].second = newColor;
             update();
         }
+        e->accept();
+    }
+    else if (handleArea.contains(e->pos())) {
+        float v = handleYToGradVal(e->pos().y());
+        points.append({ v, colorAtY(e->pos().y()) });
+        e->accept();
+        update();
     }
     else {
         e->ignore();

+ 9 - 7
GradientWidget.h

@@ -3,14 +3,14 @@
 
 #include <QWidget>
 #include <QLinearGradient>
-
-#include <utility>
+#include <QVector>
+#include <QPair>
 
 class GradientWidget :
     public QWidget
 {
     Q_OBJECT
-    QVector<std::pair<float, QColor>> points;
+    QVector<QPair<float, QColor>> points;
 
     bool dragging;
     int selectedHandle;
@@ -18,13 +18,15 @@ class GradientWidget :
 
     int mouseOver;
 
-    int handleWidth = 60;
-    int handleHeight = 30;
+    int handleWidth = 40;
+    int handleHeight = 24;
 public:
     explicit GradientWidget(QWidget *parent = nullptr);
 
-    const QVector<std::pair<float, QColor>>& getGradient(void) const;
-    void setGradient(QVector<std::pair<float, QColor>>);
+    const QVector<QPair<float, QColor>>& getGradient(void) const;
+    void setGradient(QVector<QPair<float, QColor>>);
+
+    QColor colorAtY(float y);
 
     void paintEvent(QPaintEvent* e) override;
 

+ 1 - 1
gradientchoosedialog.cpp

@@ -23,7 +23,7 @@ GradientChooseDialog::GradientChooseDialog()
 
 void GradientChooseDialog::on_buttonBox_accepted()
 {
-    chosenGradient = std::make_unique<Gradient>(Gradient::readXml(gcd.plainTextEdit->toPlainText()));
+    //chosenGradient = std::make_unique<Gradient>(Gradient::readXml(gcd.plainTextEdit->toPlainText()));
 }