File indexing completed on 2024-11-10 04:56:37

0001 /*
0002     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "colordevice.h"
0008 #include "core/colorpipelinestage.h"
0009 #include "core/colortransformation.h"
0010 #include "core/output.h"
0011 #include "utils/common.h"
0012 
0013 #include "3rdparty/colortemperature.h"
0014 
0015 #include <QTimer>
0016 
0017 #include <lcms2.h>
0018 
0019 namespace KWin
0020 {
0021 
0022 class ColorDevicePrivate
0023 {
0024 public:
0025     void recalculateFactors();
0026 
0027     Output *output;
0028     QTimer *updateTimer;
0029     uint brightness = 100;
0030     uint temperature = 6500;
0031 
0032     QVector3D temperatureFactors = QVector3D(1, 1, 1);
0033     QVector3D brightnessFactors = QVector3D(1, 1, 1);
0034 
0035     std::shared_ptr<ColorTransformation> transformation;
0036     // used if only limited per-channel multiplication is available
0037     QVector3D simpleTransformation = QVector3D(1, 1, 1);
0038 };
0039 
0040 static qreal interpolate(qreal a, qreal b, qreal blendFactor)
0041 {
0042     return (1 - blendFactor) * a + blendFactor * b;
0043 }
0044 
0045 void ColorDevicePrivate::recalculateFactors()
0046 {
0047     brightnessFactors = QVector3D(brightness / 100.0, brightness / 100.0, brightness / 100.0);
0048 
0049     if (temperature == 6500) {
0050         temperatureFactors = QVector3D(1, 1, 1);
0051     } else {
0052         // Note that cmsWhitePointFromTemp() returns a slightly green-ish white point.
0053         const int blackBodyColorIndex = ((temperature - 1000) / 100) * 3;
0054         const qreal blendFactor = (temperature % 100) / 100.0;
0055 
0056         const qreal xWhitePoint = interpolate(blackbodyColor[blackBodyColorIndex + 0],
0057                                               blackbodyColor[blackBodyColorIndex + 3],
0058                                               blendFactor);
0059         const qreal yWhitePoint = interpolate(blackbodyColor[blackBodyColorIndex + 1],
0060                                               blackbodyColor[blackBodyColorIndex + 4],
0061                                               blendFactor);
0062         const qreal zWhitePoint = interpolate(blackbodyColor[blackBodyColorIndex + 2],
0063                                               blackbodyColor[blackBodyColorIndex + 5],
0064                                               blendFactor);
0065         // the values in the blackbodyColor array are "gamma corrected", but we need a linear value
0066         temperatureFactors = ColorDescription::encodedToNits(QVector3D(xWhitePoint, yWhitePoint, zWhitePoint), NamedTransferFunction::gamma22, 1);
0067     }
0068     simpleTransformation = brightnessFactors * temperatureFactors;
0069 }
0070 
0071 ColorDevice::ColorDevice(Output *output, QObject *parent)
0072     : QObject(parent)
0073     , d(new ColorDevicePrivate)
0074 {
0075     d->updateTimer = new QTimer(this);
0076     d->updateTimer->setSingleShot(true);
0077     connect(d->updateTimer, &QTimer::timeout, this, &ColorDevice::update);
0078 
0079     d->output = output;
0080     scheduleUpdate();
0081 }
0082 
0083 ColorDevice::~ColorDevice()
0084 {
0085 }
0086 
0087 Output *ColorDevice::output() const
0088 {
0089     return d->output;
0090 }
0091 
0092 uint ColorDevice::brightness() const
0093 {
0094     return d->brightness;
0095 }
0096 
0097 void ColorDevice::setBrightness(uint brightness)
0098 {
0099     if (brightness > 100) {
0100         qCWarning(KWIN_CORE) << "Got invalid brightness value:" << brightness;
0101         brightness = 100;
0102     }
0103     if (d->brightness == brightness) {
0104         return;
0105     }
0106     d->brightness = brightness;
0107     scheduleUpdate();
0108     Q_EMIT brightnessChanged();
0109 }
0110 
0111 uint ColorDevice::temperature() const
0112 {
0113     return d->temperature;
0114 }
0115 
0116 void ColorDevice::setTemperature(uint temperature)
0117 {
0118     if (temperature > 6500) {
0119         qCWarning(KWIN_CORE) << "Got invalid temperature value:" << temperature;
0120         temperature = 6500;
0121     }
0122     if (d->temperature == temperature) {
0123         return;
0124     }
0125     d->temperature = temperature;
0126     scheduleUpdate();
0127     Q_EMIT temperatureChanged();
0128 }
0129 
0130 void ColorDevice::update()
0131 {
0132     d->recalculateFactors();
0133     d->output->setChannelFactors(d->simpleTransformation);
0134 }
0135 
0136 void ColorDevice::scheduleUpdate()
0137 {
0138     d->updateTimer->start();
0139 }
0140 
0141 } // namespace KWin
0142 
0143 #include "moc_colordevice.cpp"