JeffV
JeffV

Reputation: 54517

How to de-saturate a color defined by RGB in declarative QML

Using QtQuick, is it possible to take an existing color defined by RGB and desaturate it without resorting to Javascript?

Upvotes: 1

Views: 2076

Answers (3)

Tim Angus
Tim Angus

Reputation: 1103

function desaturate(colorString)
{
    let c = Qt.darker(colorString, 1.0);
    return Qt.hsla(c.hslHue, 0.0, c.hslLightness, c.a);
}

Upvotes: 3

UnePierre
UnePierre

Reputation: 63

Just modify the color's hsvSaturation, or hslSaturation property, depending on your color model.

Example Test.qml that de-saturates a blue rectangle gradually every second (runs with qmlscene Test.qml):

import QtQuick 2.7
Item {
    width: 100
    height: 100
    Rectangle {
        anchors.fill: parent
        objectName: "precious"
        color: "#0000FF"
        Timer {
            running: true
            repeat: true
            onTriggered: parent.color.hslSaturation *= 0.8;
        }
    }
}

Hidden in a lot of text, between two blocks of code, these properties are actually documented in color QML Basic Type. They appear to correspond to the qreal versions of color component properties in C++ (names ending in ...F()).


As @dtech states, the onTriggered:... is legally already a line of JavaScript. If you must, you can also walk down the line in C++, find the element's attribute, and modify the property there.

Example main.cpp, showing the above Test.qml, that starts with an already very de-saturated (purely in C++) blue rectangle, then gradually de-saturating it (still done in QML/JavaScript):

#include <QGuiApplication>
#include <QQuickView>
#include <QQuickItem>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQuickView view(QUrl(QLatin1String("qrc:/Test.qml")));
    view.show();
    if ( QObject * rectangle = view.rootObject()->findChild<QObject*>("precious") )
    {
        qreal h, s, l, a;
        rectangle->property("color").value<QColor>().getHslF( &h, &s, &l, &a );
        s *= 0.2;
        rectangle->setProperty( "color", QVariant::fromValue(QColor::fromHsvF( h, s, l, a )) );
    }
    return app.exec();
}

Now you could remove the apparent line of JavaScript from Test.qml.


Note that I demonstrate a very crude color animation. There is a special ColorAnimation tool to do better.

Upvotes: 1

dtech
dtech

Reputation: 49319

Seeing how QtQuick runs on top of a Javascript engine, I'd say it is not possible to do anything in QML without resorting to Javascript.

In order to adjust the saturation you will have to convert RGB to HSL color and then back to RGB. QML has Qt.hsla() for the latter, but last time I checked it didn't provide a conversion from RGB to HSL, so I ended up using this:

function rgbToHsl(r, g, b) {
  r /= 255
  g /= 255
  b /= 255
  var max = Math.max(r, g, b), min = Math.min(r, g, b)
  var h, s, l = (max + min) / 2
  if (max == min) {
    h = s = 0
  } else {
    var d = max - min
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
    switch (max) {
    case r:
      h = (g - b) / d + (g < b ? 6 : 0)
      break
    case g:
      h = (b - r) / d + 2
      break
    case b:
      h = (r - g) / d + 4
      break
    }
    h /= 6;
  }
  return {"h":h, "s":s, "l":l};
}

So you basically:

var ic = yourRGBColor.toString()
var r = parseInt(ic.substr(1, 2), 16)
var g = parseInt(ic.substr(3, 2), 16)
var b = parseInt(ic.substr(5, 2), 16)
var hsl = rgbToHsl(r, g, b)
hsl.s *= .5 // desaturate 50%
yourRGBColor = Qt.hsla(hsl.h, hsl.s, hsl.l, 1)

Upvotes: 1

Related Questions