Output device color curves correction
Summary: Extends the output device and output configuration interfaces with the ability to query and set the RGB color intensity curves (gamma ramps) of the associated output. Test Plan: Manually. Auto tests will be added to this diff soon. Reviewers: #frameworks, graesslin, romangg Subscribers: kde-frameworks-devel, graesslin, davidedmundson, zzag, cfeck Tags: #frameworks Differential Revision: https://phabricator.kde.org/D12388
This commit is contained in:
parent
fa1b65bb5a
commit
17832e8adc
8 changed files with 216 additions and 0 deletions
|
@ -46,6 +46,7 @@ private Q_SLOTS:
|
|||
void testModeChanges();
|
||||
void testScaleChange_legacy();
|
||||
void testScaleChange();
|
||||
void testColorCurvesChange();
|
||||
|
||||
void testSubPixel_data();
|
||||
void testSubPixel();
|
||||
|
@ -62,6 +63,7 @@ private:
|
|||
KWayland::Server::Display *m_display;
|
||||
KWayland::Server::OutputDeviceInterface *m_serverOutputDevice;
|
||||
QByteArray m_edid;
|
||||
KWayland::Server::OutputDeviceInterface::ColorCurves m_initColorCurves;
|
||||
KWayland::Client::ConnectionThread *m_connection;
|
||||
KWayland::Client::EventQueue *m_queue;
|
||||
QThread *m_thread;
|
||||
|
@ -115,6 +117,21 @@ void TestWaylandOutputDevice::init()
|
|||
m_edid = QByteArray::fromBase64("AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg==");
|
||||
m_serverOutputDevice->setEdid(m_edid);
|
||||
|
||||
m_initColorCurves.red.clear();
|
||||
m_initColorCurves.green.clear();
|
||||
m_initColorCurves.blue.clear();
|
||||
// 8 bit color ramps
|
||||
for (int i = 0; i < 256; i++) {
|
||||
quint16 val = (double)i / 255 * UINT16_MAX;
|
||||
m_initColorCurves.red << val ;
|
||||
m_initColorCurves.green << val ;
|
||||
}
|
||||
// 10 bit color ramp
|
||||
for (int i = 0; i < 320; i++) {
|
||||
m_initColorCurves.blue << (double)i / 319 * UINT16_MAX;
|
||||
}
|
||||
m_serverOutputDevice->setColorCurves(m_initColorCurves);
|
||||
|
||||
m_serverOutputDevice->create();
|
||||
|
||||
// setup connection
|
||||
|
@ -183,6 +200,9 @@ void TestWaylandOutputDevice::testRegistry()
|
|||
QCOMPARE(output.pixelSize(), QSize());
|
||||
QCOMPARE(output.refreshRate(), 0);
|
||||
QCOMPARE(output.scale(), 1);
|
||||
QCOMPARE(output.colorCurves().red, QVector<quint16>());
|
||||
QCOMPARE(output.colorCurves().green, QVector<quint16>());
|
||||
QCOMPARE(output.colorCurves().blue, QVector<quint16>());
|
||||
QCOMPARE(output.subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown);
|
||||
QCOMPARE(output.transform(), KWayland::Client::OutputDevice::Transform::Normal);
|
||||
QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled);
|
||||
|
@ -203,6 +223,9 @@ void TestWaylandOutputDevice::testRegistry()
|
|||
QCOMPARE(output.pixelSize(), QSize(1024, 768));
|
||||
QCOMPARE(output.refreshRate(), 60000);
|
||||
QCOMPARE(output.scale(), 1);
|
||||
QCOMPARE(output.colorCurves().red, m_initColorCurves.red);
|
||||
QCOMPARE(output.colorCurves().green, m_initColorCurves.green);
|
||||
QCOMPARE(output.colorCurves().blue, m_initColorCurves.blue);
|
||||
// for xwayland output it's unknown
|
||||
QCOMPARE(output.subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown);
|
||||
// for xwayland transform is normal
|
||||
|
@ -384,6 +407,53 @@ void TestWaylandOutputDevice::testScaleChange()
|
|||
QCOMPARE(wl_fixed_from_double(output.scaleF()), wl_fixed_from_double(4.9));
|
||||
}
|
||||
|
||||
void TestWaylandOutputDevice::testColorCurvesChange()
|
||||
{
|
||||
KWayland::Client::Registry registry;
|
||||
QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced);
|
||||
QVERIFY(interfacesAnnouncedSpy.isValid());
|
||||
QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced);
|
||||
registry.setEventQueue(m_queue);
|
||||
registry.create(m_connection->display());
|
||||
QVERIFY(registry.isValid());
|
||||
registry.setup();
|
||||
wl_display_flush(m_connection->display());
|
||||
QVERIFY(interfacesAnnouncedSpy.wait());
|
||||
|
||||
KWayland::Client::OutputDevice output;
|
||||
QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done);
|
||||
QVERIFY(outputChanged.isValid());
|
||||
output.setup(registry.bindOutputDevice(announced.first().first().value<quint32>(), announced.first().last().value<quint32>()));
|
||||
wl_display_flush(m_connection->display());
|
||||
QVERIFY(outputChanged.wait());
|
||||
QCOMPARE(output.colorCurves().red, m_initColorCurves.red);
|
||||
QCOMPARE(output.colorCurves().green, m_initColorCurves.green);
|
||||
QCOMPARE(output.colorCurves().blue, m_initColorCurves.blue);
|
||||
|
||||
// change the color curves
|
||||
outputChanged.clear();
|
||||
KWayland::Server::OutputDeviceInterface::ColorCurves cc;
|
||||
cc.red = QVector<quint16>(256, 0);
|
||||
cc.green = QVector<quint16>(256, UINT16_MAX);
|
||||
cc.blue = QVector<quint16>(320, 1);
|
||||
m_serverOutputDevice->setColorCurves(cc);
|
||||
QVERIFY(outputChanged.wait());
|
||||
QCOMPARE(output.colorCurves().red, cc.red);
|
||||
QCOMPARE(output.colorCurves().green, cc.green);
|
||||
QCOMPARE(output.colorCurves().blue, cc.blue);
|
||||
|
||||
// change once more
|
||||
outputChanged.clear();
|
||||
cc.red = QVector<quint16>(256, 0);
|
||||
cc.green = QVector<quint16>(256, UINT16_MAX);
|
||||
cc.blue = QVector<quint16>(320, UINT16_MAX);
|
||||
m_serverOutputDevice->setColorCurves(cc);
|
||||
QVERIFY(outputChanged.wait());
|
||||
QCOMPARE(output.colorCurves().red, cc.red);
|
||||
QCOMPARE(output.colorCurves().green, cc.green);
|
||||
QCOMPARE(output.colorCurves().blue, cc.blue);
|
||||
}
|
||||
|
||||
void TestWaylandOutputDevice::testSubPixel_data()
|
||||
{
|
||||
using namespace KWayland::Client;
|
||||
|
|
|
@ -250,6 +250,9 @@ void TestWaylandOutputManagement::applyPendingChanges(KWayland::Server::OutputCo
|
|||
if (c->scaleChanged()) {
|
||||
outputdevice->setScaleF(c->scaleF());
|
||||
}
|
||||
if (c->colorCurvesChanged()) {
|
||||
outputdevice->setColorCurves(c->colorCurves());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,6 +271,9 @@ void TestWaylandOutputManagement::createOutputDevices()
|
|||
QCOMPARE(output->pixelSize(), QSize());
|
||||
QCOMPARE(output->refreshRate(), 0);
|
||||
QCOMPARE(output->scale(), 1);
|
||||
QCOMPARE(output->colorCurves().red, QVector<quint16>());
|
||||
QCOMPARE(output->colorCurves().green, QVector<quint16>());
|
||||
QCOMPARE(output->colorCurves().blue, QVector<quint16>());
|
||||
QCOMPARE(output->subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown);
|
||||
QCOMPARE(output->transform(), KWayland::Client::OutputDevice::Transform::Normal);
|
||||
QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled);
|
||||
|
@ -405,6 +411,8 @@ void TestWaylandOutputManagement::testMultipleSettings()
|
|||
config->setTransform(output, OutputDevice::Transform::Rotated90);
|
||||
config->setPosition(output, QPoint(13, 37));
|
||||
config->setScale(output, 2);
|
||||
const auto zeroVector = QVector<quint16>(256, 0);
|
||||
config->setColorCurves(output, zeroVector, zeroVector, zeroVector);
|
||||
config->setEnabled(output, OutputDevice::Enablement::Disabled);
|
||||
config->apply();
|
||||
|
||||
|
@ -423,6 +431,8 @@ void TestWaylandOutputManagement::testMultipleSettings()
|
|||
config->setTransform(output, OutputDevice::Transform::Normal);
|
||||
config->setPosition(output, QPoint(0, 1920));
|
||||
config->setScale(output, 1);
|
||||
const auto oneVector = QVector<quint16>(256, 1);
|
||||
config->setColorCurves(output, oneVector, oneVector, oneVector);
|
||||
config->setEnabled(output, OutputDevice::Enablement::Enabled);
|
||||
config->apply();
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ OutputChangeSet::Private::Private(OutputDeviceInterface *outputdevice, OutputCha
|
|||
, transform(o->transform())
|
||||
, position(o->globalPosition())
|
||||
, scale(o->scale())
|
||||
, colorCurves(o->colorCurves())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -117,5 +118,17 @@ qreal OutputChangeSet::scaleF() const
|
|||
return d->scale;
|
||||
}
|
||||
|
||||
bool OutputChangeSet::colorCurvesChanged() const
|
||||
{
|
||||
Q_D();
|
||||
return d->colorCurves != d->o->colorCurves();
|
||||
}
|
||||
|
||||
OutputDeviceInterface::ColorCurves OutputChangeSet::colorCurves() const
|
||||
{
|
||||
Q_D();
|
||||
return d->colorCurves;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,10 @@ public:
|
|||
* @returns @c true if the scale() property of the outputdevice has changed.
|
||||
*/
|
||||
bool scaleChanged() const;
|
||||
/** Whether the colorCurves() property of the outputdevice changed.
|
||||
* @returns @c true if the colorCurves() property of the outputdevice has changed.
|
||||
*/
|
||||
bool colorCurvesChanged() const;
|
||||
|
||||
/** The new value for enabled. */
|
||||
OutputDeviceInterface::Enablement enabled() const;
|
||||
|
@ -86,6 +90,10 @@ public:
|
|||
* @since 5.XX
|
||||
*/
|
||||
qreal scaleF() const;
|
||||
/** The new value for colorCurves.
|
||||
* @since 5.XX
|
||||
*/
|
||||
OutputDeviceInterface::ColorCurves colorCurves() const;
|
||||
|
||||
private:
|
||||
friend class OutputConfigurationInterface;
|
||||
|
|
|
@ -41,6 +41,7 @@ namespace KWayland
|
|||
OutputDeviceInterface::Transform transform;
|
||||
QPoint position;
|
||||
qreal scale;
|
||||
OutputDeviceInterface::ColorCurves colorCurves;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,9 @@ private:
|
|||
static void applyCallback(wl_client *client, wl_resource *resource);
|
||||
static void scaleFCallback(wl_client *client, wl_resource *resource,
|
||||
wl_resource * outputdevice, wl_fixed_t scale);
|
||||
static void colorcurvesCallback(wl_client *client, wl_resource *resource,
|
||||
wl_resource * outputdevice,
|
||||
wl_array *red, wl_array *green, wl_array *blue);
|
||||
|
||||
OutputConfigurationInterface *q_func() {
|
||||
return reinterpret_cast<OutputConfigurationInterface *>(q);
|
||||
|
@ -87,6 +90,7 @@ const struct org_kde_kwin_outputconfiguration_interface OutputConfigurationInter
|
|||
scaleCallback,
|
||||
applyCallback,
|
||||
scaleFCallback,
|
||||
colorcurvesCallback,
|
||||
resourceDestroyedCallback
|
||||
};
|
||||
|
||||
|
@ -213,6 +217,42 @@ void OutputConfigurationInterface::Private::applyCallback(wl_client *client, wl_
|
|||
s->emitConfigurationChangeRequested();
|
||||
}
|
||||
|
||||
void OutputConfigurationInterface::Private::colorcurvesCallback(wl_client *client, wl_resource *resource,
|
||||
wl_resource * outputdevice,
|
||||
wl_array *red, wl_array *green, wl_array *blue)
|
||||
{
|
||||
Q_UNUSED(client);
|
||||
OutputDeviceInterface *o = OutputDeviceInterface::get(outputdevice);
|
||||
OutputDeviceInterface::ColorCurves oldCc = o->colorCurves();
|
||||
|
||||
auto checkArg = [](const wl_array *newColor, const QVector<quint16> &oldColor) {
|
||||
return (newColor->size % sizeof(uint16_t) == 0) &&
|
||||
(newColor->size / sizeof(uint16_t) == static_cast<size_t>(oldColor.size()));
|
||||
};
|
||||
if (!checkArg(red, oldCc.red) || !checkArg(green, oldCc.green) || !checkArg(blue, oldCc.blue)) {
|
||||
qCWarning(KWAYLAND_SERVER) << "Requested to change color curves, but have wrong size.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto s = cast<Private>(resource);
|
||||
Q_ASSERT(s);
|
||||
OutputDeviceInterface::ColorCurves cc;
|
||||
|
||||
auto fillVector = [](const wl_array *array, QVector<quint16> *v) {
|
||||
const uint16_t *pos = (uint16_t*)array->data;
|
||||
|
||||
while((char*)pos < (char*)array->data + array->size) {
|
||||
v->append(*pos);
|
||||
pos++;
|
||||
}
|
||||
};
|
||||
fillVector(red, &cc.red);
|
||||
fillVector(green, &cc.green);
|
||||
fillVector(blue, &cc.blue);
|
||||
|
||||
s->pendingChanges(o)->d_func()->colorCurves = cc;
|
||||
}
|
||||
|
||||
void OutputConfigurationInterface::Private::emitConfigurationChangeRequested() const
|
||||
{
|
||||
auto configinterface = reinterpret_cast<OutputConfigurationInterface *>(q);
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
void sendDone(const ResourceData &data);
|
||||
void updateGeometry();
|
||||
void updateScale();
|
||||
void updateColorCurves();
|
||||
|
||||
void sendUuid();
|
||||
void sendEdid();
|
||||
|
@ -57,6 +58,7 @@ public:
|
|||
qreal scale = 1.0;
|
||||
SubPixel subPixel = SubPixel::Unknown;
|
||||
Transform transform = Transform::Normal;
|
||||
ColorCurves colorCurves;
|
||||
QList<Mode> modes;
|
||||
QList<ResourceData> resources;
|
||||
|
||||
|
@ -74,6 +76,7 @@ private:
|
|||
int32_t toSubPixel() const;
|
||||
void sendGeometry(wl_resource *resource);
|
||||
void sendScale(const ResourceData &data);
|
||||
void sendColorCurves(const ResourceData &data);
|
||||
|
||||
static const quint32 s_version;
|
||||
OutputDeviceInterface *q;
|
||||
|
@ -139,6 +142,8 @@ OutputDeviceInterface::OutputDeviceInterface(Display *display, QObject *parent)
|
|||
connect(this, &OutputDeviceInterface::modelChanged, this, [this, d] { d->updateGeometry(); });
|
||||
connect(this, &OutputDeviceInterface::manufacturerChanged, this, [this, d] { d->updateGeometry(); });
|
||||
connect(this, &OutputDeviceInterface::scaleFChanged, this, [this, d] { d->updateScale(); });
|
||||
connect(this, &OutputDeviceInterface::scaleChanged, this, [this, d] { d->updateScale(); });
|
||||
connect(this, &OutputDeviceInterface::colorCurvesChanged, this, [this, d] { d->updateColorCurves(); });
|
||||
}
|
||||
|
||||
OutputDeviceInterface::~OutputDeviceInterface() = default;
|
||||
|
@ -333,6 +338,7 @@ void OutputDeviceInterface::Private::bind(wl_client *client, uint32_t version, u
|
|||
|
||||
sendGeometry(resource);
|
||||
sendScale(r);
|
||||
sendColorCurves(r);
|
||||
|
||||
auto currentModeIt = modes.constEnd();
|
||||
for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) {
|
||||
|
@ -409,6 +415,31 @@ void OutputDeviceInterface::Private::sendScale(const ResourceData &data)
|
|||
}
|
||||
}
|
||||
|
||||
void OutputDeviceInterface::Private::sendColorCurves(const ResourceData &data)
|
||||
{
|
||||
if (data.version < ORG_KDE_KWIN_OUTPUTDEVICE_COLORCURVES_SINCE_VERSION) {
|
||||
return;
|
||||
}
|
||||
|
||||
wl_array wlRed, wlGreen, wlBlue;
|
||||
|
||||
auto fillArray = [](const QVector<quint16> &origin, wl_array *dest) {
|
||||
wl_array_init(dest);
|
||||
const size_t memLength = sizeof(uint16_t) * origin.size();
|
||||
void *s = wl_array_add(dest, memLength);
|
||||
memcpy(s, origin.data(), memLength);
|
||||
};
|
||||
fillArray(colorCurves.red, &wlRed);
|
||||
fillArray(colorCurves.green, &wlGreen);
|
||||
fillArray(colorCurves.blue, &wlBlue);
|
||||
|
||||
org_kde_kwin_outputdevice_send_colorcurves(data.resource, &wlRed, &wlGreen, &wlBlue);
|
||||
|
||||
wl_array_release(&wlRed);
|
||||
wl_array_release(&wlGreen);
|
||||
wl_array_release(&wlBlue);
|
||||
}
|
||||
|
||||
void OutputDeviceInterface::Private::sendDone(const ResourceData &data)
|
||||
{
|
||||
org_kde_kwin_outputdevice_send_done(data.resource);
|
||||
|
@ -430,6 +461,22 @@ void OutputDeviceInterface::Private::updateScale()
|
|||
}
|
||||
}
|
||||
|
||||
void OutputDeviceInterface::Private::updateColorCurves()
|
||||
{
|
||||
for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
|
||||
sendColorCurves(*it);
|
||||
sendDone(*it);
|
||||
}
|
||||
}
|
||||
|
||||
bool OutputDeviceInterface::ColorCurves::operator==(const ColorCurves &cc) const
|
||||
{
|
||||
return red == cc.red && green == cc.green && blue == cc.blue;
|
||||
}
|
||||
bool OutputDeviceInterface::ColorCurves::operator!=(const ColorCurves &cc) const {
|
||||
return !operator==(cc);
|
||||
}
|
||||
|
||||
#define SETTER(setterName, type, argumentName) \
|
||||
void OutputDeviceInterface::setterName(type arg) \
|
||||
{ \
|
||||
|
@ -521,6 +568,12 @@ OutputDeviceInterface::Transform OutputDeviceInterface::transform() const
|
|||
return d->transform;
|
||||
}
|
||||
|
||||
OutputDeviceInterface::ColorCurves OutputDeviceInterface::colorCurves() const
|
||||
{
|
||||
Q_D();
|
||||
return d->colorCurves;
|
||||
}
|
||||
|
||||
QList< OutputDeviceInterface::Mode > OutputDeviceInterface::modes() const
|
||||
{
|
||||
Q_D();
|
||||
|
@ -543,6 +596,17 @@ OutputDeviceInterface::Private *OutputDeviceInterface::d_func() const
|
|||
return reinterpret_cast<Private*>(d.data());
|
||||
}
|
||||
|
||||
void OutputDeviceInterface::setColorCurves(const ColorCurves &colorCurves)
|
||||
{
|
||||
Q_D();
|
||||
|
||||
if (d->colorCurves == colorCurves) {
|
||||
return;
|
||||
}
|
||||
d->colorCurves = colorCurves;
|
||||
emit colorCurvesChanged(d->colorCurves);
|
||||
}
|
||||
|
||||
void OutputDeviceInterface::setEdid(const QByteArray &edid)
|
||||
{
|
||||
Q_D();
|
||||
|
|
|
@ -23,6 +23,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
#include <QVector>
|
||||
|
||||
#include <KWayland/Server/kwaylandserver_export.h>
|
||||
#include "global.h"
|
||||
|
@ -91,6 +92,11 @@ public:
|
|||
ModeFlags flags;
|
||||
int id = -1;
|
||||
};
|
||||
struct ColorCurves {
|
||||
QVector<quint16> red, green, blue;
|
||||
bool operator==(const ColorCurves &cc) const;
|
||||
bool operator!=(const ColorCurves &cc) const;
|
||||
};
|
||||
virtual ~OutputDeviceInterface();
|
||||
|
||||
QSize physicalSize() const;
|
||||
|
@ -103,6 +109,7 @@ public:
|
|||
qreal scaleF() const;
|
||||
SubPixel subPixel() const;
|
||||
Transform transform() const;
|
||||
ColorCurves colorCurves() const;
|
||||
QList<Mode> modes() const;
|
||||
int currentModeId() const;
|
||||
|
||||
|
@ -118,6 +125,7 @@ public:
|
|||
void setScaleF(qreal scale);
|
||||
void setSubPixel(SubPixel subPixel);
|
||||
void setTransform(Transform transform);
|
||||
void setColorCurves(const ColorCurves &colorCurves);
|
||||
void addMode(Mode &mode);
|
||||
void setCurrentMode(const int modeId);
|
||||
|
||||
|
@ -140,6 +148,7 @@ Q_SIGNALS:
|
|||
void scaleFChanged(qreal);
|
||||
void subPixelChanged(SubPixel);
|
||||
void transformChanged(Transform);
|
||||
void colorCurvesChanged(ColorCurves);
|
||||
void modesChanged();
|
||||
void currentModeChanged();
|
||||
|
||||
|
@ -161,5 +170,6 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::OutputDeviceInterface::ModeFlags
|
|||
Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::Enablement)
|
||||
Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::SubPixel)
|
||||
Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::Transform)
|
||||
Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::ColorCurves)
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue