diff --git a/composite.cpp b/composite.cpp
index 12bb75a8df..8fbbd5356e 100644
--- a/composite.cpp
+++ b/composite.cpp
@@ -55,6 +55,8 @@ along with this program. If not, see .
#include "compositingprefs.h"
#include "notifications.h"
+#include
+
#include
#include
@@ -140,6 +142,10 @@ void Workspace::slotCompositingOptionsInitialized()
}
#endif
+ kDebug(1212) << "Color correction:" << options->isGlColorCorrection();
+ ColorCorrection::instance()->setEnabled(options->isGlColorCorrection());
+ connect(ColorCorrection::instance(), SIGNAL(changed()), this, SLOT(addRepaintFull()));
+
scene = new SceneOpenGL(this);
// TODO: Add 30 second delay to protect against screen freezes as well
@@ -204,6 +210,7 @@ void Workspace::finishCompositing()
if (scene == NULL)
return;
m_finishingCompositing = true;
+ ColorCorrection::cleanup();
delete cm_selection;
foreach (Client * c, clients)
scene->windowClosed(c, NULL);
diff --git a/kcmkwin/kwincompositing/main.cpp b/kcmkwin/kwincompositing/main.cpp
index 7a89db49fa..b958ff6dc0 100644
--- a/kcmkwin/kwincompositing/main.cpp
+++ b/kcmkwin/kwincompositing/main.cpp
@@ -128,6 +128,7 @@ KWinCompositingConfig::KWinCompositingConfig(QWidget *parent, const QVariantList
connect(ui.glVSync, SIGNAL(toggled(bool)), this, SLOT(changed()));
connect(ui.glShaders, SIGNAL(toggled(bool)), this, SLOT(changed()));
+ connect(ui.glColorCorrection, SIGNAL(toggled(bool)), this, SLOT(changed()));
connect(m_showDetailedErrors, SIGNAL(triggered(bool)), SLOT(showDetailedEffectLoadingInformation()));
// Open the temporary config file
@@ -381,6 +382,7 @@ void KWinCompositingConfig::loadAdvancedTab()
ui.glVSync->setChecked(config.readEntry("GLVSync", true));
ui.glShaders->setChecked(!config.readEntry("GLLegacy", false));
+ ui.glColorCorrection->setChecked(config.readEntry("GLColorCorrection", false));
toogleSmoothScaleUi(ui.compositingType->currentIndex());
}
@@ -514,7 +516,7 @@ bool KWinCompositingConfig::saveAdvancedTab()
config.writeEntry("GLVSync", ui.glVSync->isChecked());
config.writeEntry("GLLegacy", !ui.glShaders->isChecked());
-
+ config.writeEntry("GLColorCorrection", ui.glColorCorrection->isChecked());
return advancedChanged;
}
@@ -752,6 +754,7 @@ void KWinCompositingConfig::defaults()
ui.glScaleFilter->setCurrentIndex(2);
ui.glVSync->setChecked(true);
ui.glShaders->setChecked(true);
+ ui.glColorCorrection->setChecked(false);
}
QString KWinCompositingConfig::quickHelp() const
diff --git a/kcmkwin/kwincompositing/main.ui b/kcmkwin/kwincompositing/main.ui
index cff0f63b16..fcac221ed2 100644
--- a/kcmkwin/kwincompositing/main.ui
+++ b/kcmkwin/kwincompositing/main.ui
@@ -778,6 +778,17 @@ p, li { white-space: pre-wrap; }
true
+ -
+
+
+ If enabled all rendering will be performed with Shaders written in the OpenGL Shading Language.
+On legacy hardware disabling Shaders can improve the performance.
+
+
+ Use OpenGL 2 Shaders
+
+
+
-
@@ -788,14 +799,16 @@ p, li { white-space: pre-wrap; }
- -
-
+
-
+
+
+ false
+
- If enabled all rendering will be performed with Shaders written in the OpenGL Shading Language.
-On legacy hardware disabling Shaders can improve the performance.
+ <html><head/><body><p>Activates color correction if possible, using the Kolor-Manager. Requires OpenGL 2 Shaders to be enabled and <span style=" text-decoration: underline;">Kolor-Manager to be installed</span>. May fail silently.</p><p><span style=" font-weight:600;">Experimental.</span></p></body></html>
- Use OpenGL 2 Shaders
+ Enable color correction (experimental)
@@ -893,12 +906,28 @@ On legacy hardware disabling Shaders can improve the performance.
setEnabled(bool)
- 161
- 64
+ 312
+ 119
- 194
- 119
+ 345
+ 146
+
+
+
+
+ glShaders
+ toggled(bool)
+ glColorCorrection
+ setEnabled(bool)
+
+
+ 204
+ 315
+
+
+ 204
+ 358
diff --git a/libkwineffects/CMakeLists.txt b/libkwineffects/CMakeLists.txt
index 14a2747d6e..97a072f016 100644
--- a/libkwineffects/CMakeLists.txt
+++ b/libkwineffects/CMakeLists.txt
@@ -29,6 +29,7 @@ set(kwin_GLUTILSLIB_SRCS
kwingltexture.cpp
kwinglutils_funcs.cpp
kwinglplatform.cpp
+ kwinglcolorcorrection.cpp
)
macro( KWIN4_ADD_GLUTILS_BACKEND name glinclude )
diff --git a/libkwineffects/kwineffects.cpp b/libkwineffects/kwineffects.cpp
index bae85e79b5..1f82da7b33 100644
--- a/libkwineffects/kwineffects.cpp
+++ b/libkwineffects/kwineffects.cpp
@@ -211,6 +211,7 @@ public:
qreal decorationOpacity;
qreal saturation;
qreal brightness;
+ qint32 screen;
};
WindowPaintData::WindowPaintData(EffectWindow* w)
@@ -223,6 +224,7 @@ WindowPaintData::WindowPaintData(EffectWindow* w)
setDecorationOpacity(1.0);
setSaturation(1.0);
setBrightness(1.0);
+ setScreen(0);
}
WindowPaintData::WindowPaintData(const WindowPaintData &other)
@@ -242,6 +244,7 @@ WindowPaintData::WindowPaintData(const WindowPaintData &other)
setDecorationOpacity(other.decorationOpacity());
setSaturation(other.saturation());
setBrightness(other.brightness());
+ setScreen(other.screen());
}
WindowPaintData::~WindowPaintData()
@@ -269,6 +272,11 @@ qreal WindowPaintData::brightness() const
return d->brightness;
}
+qint32 WindowPaintData::screen() const
+{
+ return d->screen;
+}
+
void WindowPaintData::setDecorationOpacity(qreal opacity)
{
d->decorationOpacity = opacity;
@@ -289,6 +297,11 @@ void WindowPaintData::setBrightness(qreal brightness)
d->brightness = brightness;
}
+void WindowPaintData::setScreen(qint32 screen) const
+{
+ d->screen = screen;
+}
+
qreal WindowPaintData::multiplyDecorationOpacity(qreal factor)
{
d->decorationOpacity *= factor;
diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h
index 2c2f7bf5ed..bd2e8f2897 100644
--- a/libkwineffects/kwineffects.h
+++ b/libkwineffects/kwineffects.h
@@ -1937,6 +1937,18 @@ public:
* @since 4.10
**/
qreal multiplyBrightness(qreal factor);
+ /**
+ * The screen number for which the painting should be done.
+ * This affects color correction (different screens may need different
+ * color correction lookup tables because they have different ICC profiles).
+ * @return screen for which painting should be done
+ */
+ qint32 screen() const;
+ /**
+ * @param screen New screen number
+ * A value less than 0 will indicate that a default profile should be done.
+ */
+ void setScreen(qint32 screen) const;
WindowQuadList quads;
/**
* Shader to be used for rendering, if any.
diff --git a/libkwineffects/kwinglcolorcorrection.cpp b/libkwineffects/kwinglcolorcorrection.cpp
new file mode 100644
index 0000000000..3c066fb4c7
--- /dev/null
+++ b/libkwineffects/kwinglcolorcorrection.cpp
@@ -0,0 +1,711 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2012 Casian Andrei
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+
+#include "kwinglcolorcorrection.h"
+#include "kwinglcolorcorrection_p.h"
+
+#include "kwinglutils.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+namespace KWin {
+
+/*
+ * Color lookup table
+ *
+ * The 3D lookup texture has 64 points in each dimension, using 16 bit integers.
+ * That means each active region will use 1.5MiB of texture memory.
+ */
+static const int LUT_GRID_POINTS = 64;
+static const size_t CLUT_ELEMENT_SIZE = sizeof(quint16);
+static const uint CLUT_ELEMENT_COUNT = LUT_GRID_POINTS * LUT_GRID_POINTS * LUT_GRID_POINTS * 3;
+static const size_t CLUT_DATA_SIZE = CLUT_ELEMENT_COUNT * CLUT_ELEMENT_SIZE;
+
+inline static void buildDummyClut(Clut &c)
+{
+ c.resize(CLUT_ELEMENT_COUNT);
+ quint16 *p = c.data();
+
+ for (int ib = 0; ib < LUT_GRID_POINTS; ++ ib) {
+ quint16 b = (quint16) ((float) ib / (LUT_GRID_POINTS - 1) * 65535.0 + 0.5);
+ for (int ig = 0; ig < LUT_GRID_POINTS; ++ ig) {
+ quint16 g = (quint16) ((float) ig / (LUT_GRID_POINTS - 1) * 65535.0 + 0.5);
+ for (int ir = 0; ir < LUT_GRID_POINTS; ++ ir) {
+ quint16 r = (quint16) ((float) ir / (LUT_GRID_POINTS - 1) * 65535.0 + 0.5);
+
+ *(p ++) = r;
+ *(p ++) = g;
+ *(p ++) = b;
+ }
+ }
+ }
+}
+
+
+/*
+ * Color Server Interface
+ */
+
+ColorServerInterface::ColorServerInterface(const QString &service,
+ const QString &path,
+ const QDBusConnection &connection,
+ QObject *parent)
+ : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
+ , m_versionInfoWatcher(0)
+ , m_outputClutsWatcher(0)
+ , m_regionClutsWatcher(0)
+ , m_versionInfoUpdated(false)
+ , m_outputClutsUpdated(false)
+ , m_regionClutsUpdated(false)
+ , m_versionInfo(0)
+ , m_signaledFail(false)
+{
+ qDBusRegisterMetaType< Clut >();
+ qDBusRegisterMetaType< ClutList >();
+ qDBusRegisterMetaType< RegionalClut >();
+ qDBusRegisterMetaType< RegionalClutMap >();
+
+ connect(this, SIGNAL(outputClutsChanged()), this, SLOT(update()));
+ connect(this, SIGNAL(regionClutsChanged()), this, SLOT(update()));
+}
+
+ColorServerInterface::~ColorServerInterface()
+{
+}
+
+uint ColorServerInterface::versionInfo() const
+{
+ if (!m_versionInfoUpdated)
+ kWarning(1212) << "Version info not updated";
+ return m_versionInfo;
+}
+
+const ClutList& ColorServerInterface::outputCluts() const
+{
+ return m_outputCluts;
+}
+
+const RegionalClutMap& ColorServerInterface::regionCluts() const
+{
+ return m_regionCluts;
+}
+
+void ColorServerInterface::update()
+{
+ m_versionInfoUpdated = false;
+ m_outputClutsUpdated = false;
+ m_regionClutsUpdated = false;
+ delete m_versionInfoWatcher;
+ delete m_outputClutsWatcher;
+ delete m_regionClutsWatcher;
+ m_versionInfoWatcher = new QDBusPendingCallWatcher(getVersionInfo(), this);
+ m_outputClutsWatcher = new QDBusPendingCallWatcher(getOutputCluts(), this);
+ m_regionClutsWatcher = new QDBusPendingCallWatcher(getRegionCluts(), this);
+ connect(m_versionInfoWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(callFinishedSlot(QDBusPendingCallWatcher*)));
+ connect(m_outputClutsWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(callFinishedSlot(QDBusPendingCallWatcher*)));
+ connect(m_regionClutsWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(callFinishedSlot(QDBusPendingCallWatcher*)));
+
+ m_signaledFail = false;
+}
+
+QDBusPendingReply< uint > ColorServerInterface::getVersionInfo()
+{
+ return QDBusPendingReply< uint >(asyncCall("getVersionInfo"));
+}
+
+QDBusPendingReply< ClutList > ColorServerInterface::getOutputCluts()
+{
+ return QDBusPendingReply< ClutList >(asyncCall("getOutputCluts"));
+}
+
+QDBusPendingReply< RegionalClutMap > ColorServerInterface::getRegionCluts()
+{
+ return QDBusPendingReply< RegionalClutMap >(asyncCall("getRegionCluts"));
+}
+
+void ColorServerInterface::callFinishedSlot(QDBusPendingCallWatcher *watcher)
+{
+ if (watcher == m_versionInfoWatcher) {
+ kDebug(1212) << "Version info call finished";
+ QDBusPendingReply< uint > reply = *watcher;
+ if (reply.isError()) {
+ kWarning(1212) << reply.error();
+ if (!m_signaledFail)
+ emit updateFailed();
+ m_signaledFail = true;
+ return;
+ } else {
+ m_versionInfo = reply.value();
+ m_versionInfoUpdated = true;
+ }
+ }
+
+ if (watcher == m_outputClutsWatcher) {
+ kDebug(1212) << "Output cluts call finished";
+ QDBusPendingReply< ClutList > reply = *watcher;
+ if (reply.isError()) {
+ kWarning(1212) << reply.error();
+ if (!m_signaledFail)
+ emit updateFailed();
+ m_signaledFail = true;
+ return;
+ } else {
+ m_outputCluts = reply.value();
+ m_outputClutsUpdated = true;
+ }
+ }
+
+ if (watcher == m_regionClutsWatcher) {
+ kDebug(1212) << "Region cluts call finished";
+ QDBusPendingReply< RegionalClutMap > reply = *watcher;
+ if (reply.isError()) {
+ kWarning(1212) << reply.error();
+ if (!m_signaledFail)
+ emit updateFailed();
+ m_signaledFail = true;
+ return;
+ } else {
+ m_regionCluts = reply.value();
+ m_regionClutsUpdated = true;
+ }
+ }
+
+ if (m_versionInfoUpdated &&
+ m_outputClutsUpdated &&
+ m_regionClutsUpdated) {
+ kDebug(1212) << "Update succeeded";
+ emit updateSucceeded();
+ }
+}
+
+
+/*
+ * To be injected in the fragment shader sources
+ */
+static const char s_ccVars[] =
+ "uniform sampler3D u_ccLookupTexture;\n";
+static const char s_ccAlteration[] =
+ "gl_FragColor.rgb = texture3D(u_ccLookupTexture, gl_FragColor.rgb * 63.0 / 64.0).rgb;\n";
+
+
+/*
+ * Color Correction
+ */
+
+ColorCorrection *ColorCorrection::s_colorCorrection = NULL;
+
+ColorCorrection *ColorCorrection::instance()
+{
+ if (!s_colorCorrection)
+ s_colorCorrection = new ColorCorrection;
+ return s_colorCorrection;
+}
+
+void ColorCorrection::cleanup()
+{
+ delete s_colorCorrection;
+ s_colorCorrection = NULL;
+}
+
+ColorCorrection::ColorCorrection()
+ : QObject()
+ , d_ptr(new ColorCorrectionPrivate(this))
+{
+
+}
+
+ColorCorrection::~ColorCorrection()
+{
+ setEnabled(false);
+}
+
+ColorCorrectionPrivate::ColorCorrectionPrivate(ColorCorrection *parent)
+ : QObject(parent)
+ , m_enabled(false)
+ , m_hasError(false)
+ , m_ccTextureUnit(-1)
+ , m_dummyCCTexture(0)
+ , m_lastOutput(-1)
+ , q_ptr(parent)
+{
+ // We need a dummy color lookup table (sRGB profile to sRGB profile)
+ buildDummyClut(m_dummyClut);
+
+ // Establish a D-Bus communication interface with KolorServer
+ m_csi = new ColorServerInterface(
+ "org.kde.kded",
+ "/modules/kolorserver",
+ QDBusConnection::sessionBus(),
+ this);
+
+ m_outputCluts = &m_csi->outputCluts();
+ m_regionCluts = &m_csi->regionCluts();
+
+ connect(m_csi, SIGNAL(updateSucceeded()), this, SLOT(colorServerUpdateSucceededSlot()));
+ connect(m_csi, SIGNAL(updateFailed()), this, SLOT(colorServerUpdateFailedSlot()));
+}
+
+ColorCorrectionPrivate::~ColorCorrectionPrivate()
+{
+
+}
+
+void ColorCorrection::setEnabled(bool enabled)
+{
+ Q_D(ColorCorrection);
+
+ if (enabled == d->m_enabled)
+ return;
+
+ if (enabled && d->m_hasError) {
+ kError(1212) << "cannot enable color correction";
+ return;
+ }
+
+#ifdef KWIN_HAVE_OPENGLES
+ if (enabled) {
+ kWarning(1212) << "color correction is not supported with OpenGL ES at the moment.";
+ return;
+ }
+#else
+ if (enabled) {
+ // Update all profiles and regions
+ d->m_csi->update();
+ } else {
+ d->deleteCCTextures();
+ }
+#endif
+
+ d->m_enabled = enabled;
+ kDebug(1212) << enabled;
+}
+
+QMap::const_iterator ColorCorrection::regionsBegin(Window w)
+{
+ Q_D(ColorCorrection);
+ return d->m_windowRegions.constFind(w);
+}
+
+QMap::const_iterator ColorCorrection::regionsEnd(Window w)
+{
+ Q_D(ColorCorrection);
+ QMap::const_iterator it = d->m_windowRegions.constFind(w);
+ while (it != d->m_windowRegions.constEnd() && it.key() == w) ++it;
+ return it;
+}
+
+void ColorCorrection::setupForOutput(int screen)
+{
+ Q_D(ColorCorrection);
+
+ if (!d->m_enabled)
+ return;
+
+ GLShader *shader = ShaderManager::instance()->getBoundShader();
+ if (!shader) {
+ kError(1212) << "no bound shader for color correction setup";
+ return;
+ }
+
+ if (!shader->setUniform("u_ccLookupTexture", d->m_ccTextureUnit)) {
+ kError(1212) << "unable to set uniform for the color correction lookup texture";
+ }
+
+#ifndef KWIN_HAVE_OPENGLES
+ d->setupCCTextures();
+
+ GLint activeTexture;
+ glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture);
+ glActiveTexture(GL_TEXTURE0 + d->m_ccTextureUnit);
+ glEnable(GL_TEXTURE_3D);
+
+ if (screen < 0 || screen >= d->m_outputCCTextures.count()) {
+ // Configure with a dummy texture in case something is wrong
+ Q_ASSERT(d->m_dummyCCTexture != 0);
+ glBindTexture(GL_TEXTURE_3D, d->m_dummyCCTexture);
+ } else {
+ // Everything looks ok, configure with the proper color correctiont texture
+ glBindTexture(GL_TEXTURE_3D, d->m_outputCCTextures[screen]);
+ }
+
+ glActiveTexture(activeTexture);
+#else
+ Q_UNUSED(screen);
+#endif // KWIN_HAVE_OPENGLES
+
+ d->m_lastOutput = screen;
+}
+
+void ColorCorrection::setupForRegion(const QMap::const_iterator ®ionIt)
+{
+#ifndef KWIN_HAVE_OPENGLES
+ Q_D(ColorCorrection);
+ const QRect *key = &(*regionIt);
+ GLuint tex = d->m_regionCCTextures.value(key, d->m_dummyCCTexture);
+
+ // Bind the correct color lookup texture
+ GLint activeTexture;
+ glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture);
+ glActiveTexture(GL_TEXTURE0 + d->m_ccTextureUnit);
+ glEnable(GL_TEXTURE_3D);
+ glBindTexture(GL_TEXTURE_3D, tex);
+ glActiveTexture(activeTexture);
+#else
+ Q_UNUSED(regionIt);
+#endif
+}
+
+void ColorCorrection::resetForRegion()
+{
+ Q_D(ColorCorrection);
+ setupForOutput(d->m_lastOutput);
+}
+
+void ColorCorrection::reset()
+{
+ setupForOutput(-1);
+}
+
+QByteArray ColorCorrection::prepareFragmentShader(const QByteArray &sourceCode)
+{
+ Q_D(ColorCorrection);
+
+ if (!d->m_enabled)
+ return sourceCode;
+
+ bool sourceIsValid = true;
+
+ /*
+ * Detect comments to ignore them later
+ */
+ QList< QPair< int, int > > comments;
+ int beginIndex, endIndex = 0;
+ int i1, i2;
+
+ enum {ctNone, ctBegin, ctEnd} commentType;
+ QByteArrayMatcher commentBegin1("/*"), commentBegin2("//");
+ QByteArrayMatcher commentEnd1("*/"), commentEnd2("\n");
+
+ do {
+ // Determine the next comment begin index
+ i1 = commentBegin1.indexIn(sourceCode, endIndex);
+ i2 = commentBegin2.indexIn(sourceCode, endIndex);
+ if (i1 == -1 && i2 == -1) commentType = ctNone;
+ else if (i1 == -1) commentType = ctEnd;
+ else if (i2 == -1) commentType = ctBegin;
+ else if (i1 < i2) commentType = ctBegin;
+ else commentType = ctEnd;
+ if (commentType == ctNone)
+ break;
+
+ // Determine the comment's end index
+ if (commentType == ctBegin) {
+ beginIndex = i1;
+ endIndex = commentEnd1.indexIn(sourceCode, beginIndex + 2);
+ }
+ if (commentType == ctEnd) {
+ beginIndex = i2;
+ endIndex = commentEnd2.indexIn(sourceCode, beginIndex + 2);
+ }
+
+ if (endIndex != -1) {
+ if (commentType == ctBegin)
+ endIndex ++; // adjust for "*/" to be removed
+ if (commentType == ctEnd)
+ endIndex --; // adjust for "\n" to be kept
+ comments.append(QPair< int, int >(beginIndex, endIndex));
+ } else {
+ if (commentType == ctBegin)
+ sourceIsValid = false;
+ if (commentType == ctEnd)
+ comments.append(QPair< int, int >(beginIndex, sourceCode.length()));
+ break;
+ }
+ } while (sourceIsValid);
+ if (!sourceIsValid)
+ return sourceCode;
+
+ // Create a version of the source code with the comments stripped out
+ QByteArray cfSource(sourceCode); // comment-free source code
+ for (int i = comments.size() - 1; i >= 0; -- i) {
+ beginIndex = comments[i].first;
+ endIndex = comments[i].second;
+ cfSource.replace(beginIndex, endIndex - beginIndex + 1, " ");
+ }
+
+ /*
+ * Browse through the code while counting braces
+ * Search for "void main() { ... }:
+ */
+ QByteArrayMatcher braceOpen("{");
+ QByteArrayMatcher braceClose("}");
+ QByteArrayMatcher voidKeyword("void");
+ int levelOfScope = 0;
+ enum {brNone, brOpen, brClose} braceType;
+
+ int mainFuncBegin = -1; // where "void main" begins
+ int mainFuncEnd = -1; // at the closing brace of "void main"
+ bool insideMainFunc = false;
+ int i = 0;
+
+ do {
+ // Determine where the next brace is
+ i1 = braceOpen.indexIn(cfSource, i);
+ i2 = braceClose.indexIn(cfSource, i);
+ if (i1 == -1 && i2 == -1) braceType = brNone;
+ else if (i1 == -1) braceType = brClose;
+ else if (i2 == -1) braceType = brOpen;
+ else if (i1 < i2) braceType = brOpen;
+ else braceType = brClose;
+ if (braceType == brNone) {
+ if (levelOfScope > 0)
+ sourceIsValid = false;
+ break;
+ }
+
+ // Handle opening brance (see if is from void main())
+ if (braceType == brOpen) {
+ if (levelOfScope == 0) {
+ // Need to search between i and i1 (the last '}' and the current '{'
+ QByteArray section = cfSource.mid(i, i1 - i);
+ int i_void = -1;
+ while ((i_void = section.indexOf("void", i_void + 1)) != -1) {
+ // Extract the subsection that begins with "void"
+ QByteArray subSection = section.mid(i_void).simplified();
+ subSection.replace('(', " ( ");
+ subSection.replace(')', " ) ");
+ QList tokens = subSection.split(' ');
+ for (int i_token = tokens.size() - 1; i_token >= 0; -- i_token)
+ if (tokens[i_token].trimmed().isEmpty())
+ tokens.removeAt(i_token);
+ if (tokens.size() == 4 &&
+ tokens[0] == "void" &&
+ tokens[1] == "main" &&
+ tokens[2] == "(" &&
+ tokens[3] == ")") {
+ if (mainFuncBegin != -1) {
+ sourceIsValid = false;
+ break;
+ }
+ mainFuncBegin = i + i_void;
+ insideMainFunc = true;
+ }
+ }
+ }
+
+ levelOfScope ++;
+ i = i1 + 1;
+ }
+
+ // Handle closing brace (see if it is from void main())
+ if (braceType == brClose) {
+ levelOfScope --;
+ if (levelOfScope < 0) {
+ sourceIsValid = false;
+ break;
+ }
+
+ if (levelOfScope == 0 && insideMainFunc) {
+ mainFuncEnd = i2;
+ insideMainFunc = false;
+ }
+
+ i = i2 + 1;
+ }
+ } while (sourceIsValid);
+ sourceIsValid = sourceIsValid && mainFuncBegin != -1 && mainFuncEnd != -1;
+ if (!sourceIsValid)
+ return sourceCode;
+
+ QByteArray mainFunc = cfSource.mid(mainFuncBegin, mainFuncEnd - mainFuncBegin + 1);
+
+ /*
+ * Insert color correction variables at the beginning and
+ * the color correction code at the end of the main function.
+ * Need to handle return "jumps" inside the main function.
+ */
+ mainFunc.insert(mainFunc.size() - 1, s_ccAlteration);
+ mainFunc.insert(0, s_ccVars);
+
+ // Search for return statements inside the main function
+ QByteArrayMatcher returnMatcher("return");
+ i = -1;
+ while ((i = returnMatcher.indexIn(mainFunc, i)) != -1) {
+ i1 = mainFunc.indexOf(';', i);
+ mainFunc.insert(i1 + 1, '}');
+ mainFunc.insert(i, '{');
+ mainFunc.insert(i + 1, s_ccAlteration);
+ mainFuncEnd += strlen(s_ccAlteration) + 2;
+
+ i = i1 + strlen(s_ccAlteration) + 2;
+ }
+
+ // Replace the main function
+ cfSource.replace(mainFuncBegin, mainFuncEnd - mainFuncBegin + 1, mainFunc);
+
+ return cfSource;
+}
+
+void ColorCorrectionPrivate::setupCCTextures()
+{
+ Q_Q(ColorCorrection);
+
+ if (m_ccTextureUnit < 0) {
+ GLint maxUnits = 0;
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxUnits);
+
+ if (maxUnits < 2) {
+ kWarning(1212) << "insufficient maximum number of texture units allowed:" << maxUnits;
+ kWarning(1212) << "color correction will be disabled";
+ m_hasError = true;
+ m_ccTextureUnit = 0;
+ q->setEnabled(false);
+ return;
+ }
+
+ m_ccTextureUnit = maxUnits - 1;
+ }
+
+ // Dummy texture first
+ if (!m_dummyCCTexture) {
+ glGenTextures(1, &m_dummyCCTexture);
+ setupCCTexture(m_dummyCCTexture, m_dummyClut);
+ }
+
+ // Setup actual color correction textures
+ if (m_outputCCTextures.isEmpty() && !m_outputCluts->isEmpty()) {
+ kDebug(1212) << "setting up output color correction textures";
+
+ const int outputCount = m_outputCluts->size();
+ m_outputCCTextures.resize(outputCount);
+ glGenTextures(outputCount, m_outputCCTextures.data());
+
+ for (int i = 0; i < outputCount; ++i)
+ setupCCTexture(m_outputCCTextures[i], m_outputCluts->at(i));
+ }
+
+ if (m_regionCCTextures.isEmpty() && !m_regionCluts->isEmpty()) {
+ Q_ASSERT(m_windowRegions.isEmpty());
+ kDebug(1212) << "setting up region color correction textures";
+
+ // Generate region textures (and place them inside a map)
+ RegionalClutMap::const_iterator rcit;
+ QMap::const_iterator rit;
+ for (rcit = m_regionCluts->begin(); rcit != m_regionCluts->end(); ++rcit) {
+ GLuint tex;
+ glGenTextures(1, &tex);
+ setupCCTexture(tex, rcit->c);
+
+ // Insert the new texture into the region maps
+ rit = m_windowRegions.insert(rcit.key(), rcit->r); // this one maps Window -> QRect
+ m_regionCCTextures.insert(&(*rit), tex); // this one maps QRect* -> texture
+ }
+ }
+
+ // TODO Handle errors (what if a texture isn't generated?)
+}
+
+void ColorCorrectionPrivate::deleteCCTextures()
+{
+ // Delete dummy texture
+ if (m_dummyCCTexture) {
+ glDeleteTextures(1, &m_dummyCCTexture);
+ m_dummyCCTexture = 0;
+ }
+
+ // Delete actual color correction extures
+ if (!m_outputCCTextures.isEmpty()) {
+ glDeleteTextures(m_outputCCTextures.size(), m_outputCCTextures.data());
+ m_outputCCTextures.clear();
+ }
+ if (!m_regionCCTextures.isEmpty()) {
+ QMap::const_iterator it;
+ for (it = m_regionCCTextures.begin(); it != m_regionCCTextures.end(); ++it)
+ glDeleteTextures(1, &(*it));
+ m_regionCCTextures.clear();
+ m_windowRegions.clear();
+ }
+}
+
+void ColorCorrectionPrivate::setupCCTexture(GLuint texture, const Clut& clut)
+{
+ if ((uint) clut.size() != CLUT_ELEMENT_COUNT) {
+ kError(1212) << "cannot setup CC texture: invalid color lookup table";
+ return;
+ }
+
+ kDebug(1212) << texture;
+
+#ifndef KWIN_HAVE_OPENGLES
+ glEnable(GL_TEXTURE_3D);
+ glBindTexture(GL_TEXTURE_3D, texture);
+
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16,
+ LUT_GRID_POINTS, LUT_GRID_POINTS, LUT_GRID_POINTS,
+ 0, GL_RGB, GL_UNSIGNED_SHORT, clut.data());
+
+ glDisable(GL_TEXTURE_3D);
+
+ checkGLError("setupCCTexture");
+#else
+ Q_UNUSED(texture);
+#endif // KWIN_HAVE_OPENGLES
+}
+
+void ColorCorrectionPrivate::colorServerUpdateSucceededSlot()
+{
+ Q_Q(ColorCorrection);
+
+ kDebug(1212) << "Update of color profiles succeeded";
+
+ // Force the color correction textures to be recreated
+ deleteCCTextures();
+
+ emit q->changed();
+}
+
+void ColorCorrectionPrivate::colorServerUpdateFailedSlot()
+{
+ Q_Q(ColorCorrection);
+
+ kError(1212) << "Update of color profiles failed";
+
+ q->setEnabled(false);
+}
+
+} // KWin namespace
diff --git a/libkwineffects/kwinglcolorcorrection.h b/libkwineffects/kwinglcolorcorrection.h
new file mode 100644
index 0000000000..c2b299745a
--- /dev/null
+++ b/libkwineffects/kwinglcolorcorrection.h
@@ -0,0 +1,143 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2012 Casian Andrei
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+
+#ifndef KWIN_COLOR_CORRECTION_H
+#define KWIN_COLOR_CORRECTION_H
+
+#include "kwinglutils_funcs.h"
+
+#include
+#include
+#include
+
+namespace KWin {
+
+class ColorCorrectionPrivate;
+
+/**
+ * Implements a color correction mechanism. The settings are obtained
+ * asynchronously via D-Bus from kolor-server, which is part of kolor-manager.
+ *
+ * If it fails to get the settings, nothing should happen (no correction), even
+ * if it is set to enabled.
+ *
+ * Supports per-output and per-region correction (window region).
+ *
+ * \warning This class is not designed to be used by effects, however
+ * it may happen to be useful their case somehow.
+ */
+class KWIN_EXPORT ColorCorrection : public QObject
+{
+ Q_OBJECT
+
+public:
+ static ColorCorrection *instance();
+ static void cleanup();
+
+ /**
+ * The beginning of a region list for a window \param w
+ * \return Constant iterator to the beginning
+ * \see setupForRegion
+ *
+ * It can be used to indicate a particular region for which
+ * to set up color correction.
+ *
+ * These regions come from the applications, which communicate with the
+ * color server (kolor-server in kolor-manager), and this has no direct
+ * relation to KWin. Consequently, modifying these regions does not
+ * make sense inside KWin.
+ */
+ QMap::const_iterator regionsBegin(Window w);
+
+ /**
+ * The ending of a region list for a window \param w
+ * \return Constant iterator to the ending
+ * \see setupForRegion
+ */
+ QMap::const_iterator regionsEnd(Window w);
+
+ /**
+ * Prepares color correction for the output number \param screen.
+ * Sets up the appropriate color lookup texture for the output.
+ */
+ void setupForOutput(int screen);
+
+ /**
+ * Prepares color correction for one region of a window (in fact a rect),
+ * which is indicated by \param regionIt. This can be obtained by iterating
+ * between \ref regionsBegin and \ref regionsEnd, for that particular window.
+ *
+ * \note This should be called when drawing a window
+ */
+ void setupForRegion(const QMap::const_iterator ®ionIt);
+
+ /**
+ * Disables color correction for a particular window region. Instead,
+ * color correction is set up again for the last output that was set
+ * with \ref setupForOutput.
+ *
+ * \note This should be called after finishing the drawing of a window.
+ */
+ void resetForRegion();
+
+ /**
+ * Unsets color correction by using a dummy color lookup texture. This
+ * does not disable anything, the CC mechanisms remain in place. Instead, it
+ * indicates to draw normally.
+ */
+ void reset();
+
+ /**
+ * When color correction is disabled, it does nothing and returns
+ * \param sourceCode.
+ *
+ * Else, it modifies \param sourceCode, making it suitable for performing
+ * color correction. This is done by inserting a 3d texture lookup operation
+ * just before the output fragment color is returned.
+ */
+ QByteArray prepareFragmentShader(const QByteArray &sourceCode);
+
+public slots:
+ /**
+ * Enables or disables color correction. Compositing should be restarted
+ * for changes to take effect.
+ */
+ void setEnabled(bool enabled);
+
+signals:
+ /**
+ * Emitted when some changes happened to the color correction settings, and
+ * a full repaint of the scene should be done to make the new settings visible.
+ */
+ void changed();
+
+private:
+ ColorCorrection();
+ virtual ~ColorCorrection();
+
+private:
+ ColorCorrectionPrivate *d_ptr;
+ Q_DECLARE_PRIVATE(ColorCorrection)
+ static ColorCorrection *s_colorCorrection;
+};
+
+} // KWin namespace
+
+#endif // KWIN_COLOR_CORRECTION_H
diff --git a/libkwineffects/kwinglcolorcorrection_p.h b/libkwineffects/kwinglcolorcorrection_p.h
new file mode 100644
index 0000000000..e244d01035
--- /dev/null
+++ b/libkwineffects/kwinglcolorcorrection_p.h
@@ -0,0 +1,168 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2012 Casian Andrei
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*********************************************************************/
+
+#ifndef KWIN_COLOR_CORRECTION_P_H_
+#define KWIN_COLOR_CORRECTION_P_H_
+
+#include "kwinglcolorcorrection.h"
+
+#include
+#include
+#include
+#include
+#include
+
+class QDBusPendingCallWatcher;
+
+/*
+ * Clut
+ * All this should be the same as in the color server code, in kolor-manager
+ */
+typedef QVector Clut;
+typedef QList ClutList;
+typedef struct { QRect r; Clut c; } RegionalClut;
+typedef QMultiMap RegionalClutMap;
+
+Q_DECLARE_METATYPE(Clut)
+Q_DECLARE_METATYPE(ClutList)
+Q_DECLARE_METATYPE(RegionalClut)
+Q_DECLARE_METATYPE(RegionalClutMap)
+
+// Marshall the RegionalClut data into a D-Bus argument
+inline QDBusArgument &operator<<(QDBusArgument &argument, const RegionalClut &rc)
+{
+ argument.beginStructure();
+ argument << rc.r << rc.c;
+ argument.endStructure();
+ return argument;
+}
+
+// Retrieve the RegionalClut data from the D-Bus argument
+inline const QDBusArgument &operator>>(const QDBusArgument &argument, RegionalClut &rc)
+{
+ argument.beginStructure();
+ argument >> rc.r >> rc.c;
+ argument.endStructure();
+ return argument;
+}
+
+
+namespace KWin {
+
+class ColorServerInterface;
+
+
+/*
+ * Color Correction Private Data
+ */
+class ColorCorrectionPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ColorCorrectionPrivate(ColorCorrection* parent);
+ virtual ~ColorCorrectionPrivate();
+
+ void setupCCTextures();
+ void deleteCCTextures();
+ static void setupCCTexture(GLuint texture, const Clut &clut);
+
+public slots:
+ void colorServerUpdateSucceededSlot();
+ void colorServerUpdateFailedSlot();
+
+public:
+ bool m_enabled;
+ bool m_hasError;
+ int m_ccTextureUnit;
+
+ ColorServerInterface *m_csi;
+ const ClutList *m_outputCluts;
+ QVector m_outputCCTextures;
+ const RegionalClutMap *m_regionCluts;
+ QMultiMap m_windowRegions;
+ QMap m_regionCCTextures; // keys from m_regions's values
+ Clut m_dummyClut;
+ GLuint m_dummyCCTexture;
+
+ int m_lastOutput;
+
+private:
+ ColorCorrection *q_ptr;
+ Q_DECLARE_PUBLIC(ColorCorrection);
+};
+
+
+/*
+ * Color Server DBus interface
+ */
+class ColorServerInterface : public QDBusAbstractInterface
+{
+ Q_OBJECT
+
+public:
+ static inline const char *staticInterfaceName()
+ { return "org.kde.KolorServer"; }
+
+public:
+ ColorServerInterface(const QString &service,
+ const QString &path,
+ const QDBusConnection &connection,
+ QObject *parent = 0);
+ virtual ~ColorServerInterface();
+
+ uint versionInfo() const;
+ const ClutList& outputCluts() const;
+ const RegionalClutMap& regionCluts() const;
+
+public slots:
+ void update();
+
+signals:
+ void updateSucceeded();
+ void updateFailed();
+ void outputClutsChanged();
+ void regionClutsChanged();
+
+private:
+ QDBusPendingReply< uint > getVersionInfo();
+ QDBusPendingReply< ClutList > getOutputCluts();
+ QDBusPendingReply< RegionalClutMap > getRegionCluts();
+
+private slots:
+ void callFinishedSlot(QDBusPendingCallWatcher *watcher);
+
+private:
+ QDBusPendingCallWatcher *m_versionInfoWatcher;
+ QDBusPendingCallWatcher *m_outputClutsWatcher;
+ QDBusPendingCallWatcher *m_regionClutsWatcher;
+ bool m_versionInfoUpdated;
+ bool m_outputClutsUpdated;
+ bool m_regionClutsUpdated;
+ uint m_versionInfo;
+ ClutList m_outputCluts;
+ RegionalClutMap m_regionCluts;
+
+ bool m_signaledFail;
+};
+
+} // KWin namespace
+
+#endif // KWIN_COLOR_CORRECTION_P_H_
diff --git a/libkwineffects/kwinglutils.cpp b/libkwineffects/kwinglutils.cpp
index 016d31efc8..edddc0d6a1 100644
--- a/libkwineffects/kwinglutils.cpp
+++ b/libkwineffects/kwinglutils.cpp
@@ -24,6 +24,7 @@ along with this program. If not, see .
// need to call GLTexturePrivate::initStatic()
#include "kwingltexture_p.h"
+#include "kwinglcolorcorrection.h"
#include "kwinglobals.h"
#include "kwineffects.h"
#include "kwinglplatform.h"
@@ -304,10 +305,8 @@ bool GLShader::loadFromFiles(const QString &vertexFile, const QString &fragmentF
return load(vertexSource, fragmentSource);
}
-bool GLShader::compile(GLuint program, GLenum shaderType, const QByteArray &source) const
+const QByteArray GLShader::prepareSource(GLenum shaderType, const QByteArray &source) const
{
- GLuint shader = glCreateShader(shaderType);
-
// Prepare the source code
QByteArray ba;
#ifdef KWIN_HAVE_OPENGLES
@@ -318,7 +317,19 @@ bool GLShader::compile(GLuint program, GLenum shaderType, const QByteArray &sour
}
ba.append(source);
- const char* src = ba.constData();
+ // Inject color correction code for fragment shaders, if possible
+ if (shaderType == GL_FRAGMENT_SHADER)
+ ba = ColorCorrection::instance()->prepareFragmentShader(ba);
+
+ return ba;
+}
+
+bool GLShader::compile(GLuint program, GLenum shaderType, const QByteArray &source) const
+{
+ GLuint shader = glCreateShader(shaderType);
+
+ QByteArray preparedSource = prepareSource(shaderType, source);
+ const char* src = preparedSource.constData();
glShaderSource(shader, 1, &src, NULL);
// Compile the shader
diff --git a/libkwineffects/kwinglutils.h b/libkwineffects/kwinglutils.h
index 7a7c3c96ee..7f092a5772 100644
--- a/libkwineffects/kwinglutils.h
+++ b/libkwineffects/kwinglutils.h
@@ -204,6 +204,7 @@ protected:
GLShader();
bool loadFromFiles(const QString& vertexfile, const QString& fragmentfile);
bool load(const QByteArray &vertexSource, const QByteArray &fragmentSource);
+ const QByteArray prepareSource(GLenum shaderType, const QByteArray &sourceCode) const;
bool compile(GLuint program, GLenum shaderType, const QByteArray &sourceCode) const;
void bind();
void unbind();
diff --git a/options.cpp b/options.cpp
index 72f168d94a..bba9eb7687 100644
--- a/options.cpp
+++ b/options.cpp
@@ -148,6 +148,7 @@ Options::Options(QObject *parent)
, m_unredirectFullscreen(Options::defaultUnredirectFullscreen())
, m_glSmoothScale(Options::defaultGlSmoothScale())
, m_glVSync(Options::defaultGlVSync())
+ , m_glColorCorrection(Options::defaultGlColorCorrection())
, m_xrenderSmoothScale(Options::defaultXrenderSmoothScale())
, m_maxFpsInterval(Options::defaultMaxFpsInterval())
, m_refreshRate(Options::defaultRefreshRate())
@@ -697,6 +698,15 @@ void Options::setGlVSync(bool glVSync)
emit glVSyncChanged();
}
+void Options::setGlColorCorrection(bool glColorCorrection)
+{
+ if (m_glColorCorrection == glColorCorrection) {
+ return;
+ }
+ m_glColorCorrection = glColorCorrection;
+ emit glColorCorrectionChanged();
+}
+
void Options::setXrenderSmoothScale(bool xrenderSmoothScale)
{
if (m_xrenderSmoothScale == xrenderSmoothScale) {
@@ -979,6 +989,7 @@ void Options::reloadCompositingSettings(bool force)
setGlDirect(prefs.enableDirectRendering());
setGlVSync(config.readEntry("GLVSync", Options::defaultGlVSync()));
+ setGlColorCorrection(config.readEntry("GLColorCorrection", Options::defaultGlColorCorrection()));
setGlSmoothScale(qBound(-1, config.readEntry("GLTextureFilter", Options::defaultGlSmoothScale()), 2));
setGlStrictBindingFollowsDriver(!config.hasKey("GLStrictBinding"));
if (!isGlStrictBindingFollowsDriver()) {
diff --git a/options.h b/options.h
index 9e4fcc682e..42e0e4abb7 100644
--- a/options.h
+++ b/options.h
@@ -179,6 +179,7 @@ class Options : public QObject, public KDecorationOptions
**/
Q_PROPERTY(int glSmoothScale READ glSmoothScale WRITE setGlSmoothScale NOTIFY glSmoothScaleChanged)
Q_PROPERTY(bool glVSync READ isGlVSync WRITE setGlVSync NOTIFY glVSyncChanged)
+ Q_PROPERTY(bool glColorCorrection READ isGlColorCorrection WRITE setGlColorCorrection NOTIFY glColorCorrectionChanged)
Q_PROPERTY(bool xrenderSmoothScale READ isXrenderSmoothScale WRITE setXrenderSmoothScale NOTIFY xrenderSmoothScaleChanged)
Q_PROPERTY(uint maxFpsInterval READ maxFpsInterval WRITE setMaxFpsInterval NOTIFY maxFpsIntervalChanged)
Q_PROPERTY(uint refreshRate READ refreshRate WRITE setRefreshRate NOTIFY refreshRateChanged)
@@ -538,6 +539,9 @@ public:
bool isGlVSync() const {
return m_glVSync;
}
+ bool isGlColorCorrection() const {
+ return m_glColorCorrection;
+ }
// XRender
bool isXrenderSmoothScale() const {
return m_xrenderSmoothScale;
@@ -617,6 +621,7 @@ public:
void setUnredirectFullscreen(bool unredirectFullscreen);
void setGlSmoothScale(int glSmoothScale);
void setGlVSync(bool glVSync);
+ void setGlColorCorrection(bool glColorCorrection);
void setXrenderSmoothScale(bool xrenderSmoothScale);
void setMaxFpsInterval(uint maxFpsInterval);
void setRefreshRate(uint refreshRate);
@@ -819,6 +824,9 @@ public:
static bool defaultGlVSync() {
return true;
}
+ static bool defaultGlColorCorrection() {
+ return false;
+ }
static bool defaultXrenderSmoothScale() {
return false;
}
@@ -916,6 +924,7 @@ Q_SIGNALS:
void unredirectFullscreenChanged();
void glSmoothScaleChanged();
void glVSyncChanged();
+ void glColorCorrectionChanged();
void xrenderSmoothScaleChanged();
void maxFpsIntervalChanged();
void refreshRateChanged();
@@ -958,6 +967,7 @@ private:
bool m_unredirectFullscreen;
int m_glSmoothScale;
bool m_glVSync;
+ bool m_glColorCorrection;
bool m_xrenderSmoothScale;
uint m_maxFpsInterval;
// Settings that should be auto-detected
diff --git a/scene.cpp b/scene.cpp
index 578251014c..f4a3bdf531 100644
--- a/scene.cpp
+++ b/scene.cpp
@@ -75,12 +75,10 @@ along with this program. If not, see .
#include
#include
-#include
#include "client.h"
#include "deleted.h"
#include "effects.h"
-#include "lanczosfilter.h"
#include "overlaywindow.h"
#include "shadow.h"
@@ -437,16 +435,7 @@ void Scene::finalPaintWindow(EffectWindowImpl* w, int mask, QRegion region, Wind
// will be eventually called from drawWindow()
void Scene::finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data)
{
- if (mask & PAINT_WINDOW_LANCZOS) {
- if (lanczos_filter.isNull()) {
- lanczos_filter = new LanczosFilter(this);
- // recreate the lanczos filter when the screen gets resized
- connect(QApplication::desktop(), SIGNAL(screenCountChanged(int)), lanczos_filter.data(), SLOT(deleteLater()));
- connect(QApplication::desktop(), SIGNAL(resized(int)), lanczos_filter.data(), SLOT(deleteLater()));
- }
- lanczos_filter.data()->performPaint(w, mask, region, data);
- } else
- w->sceneWindow()->performPaint(mask, region, data);
+ w->sceneWindow()->performPaint(mask, region, data);
}
OverlayWindow* Scene::overlayWindow()
diff --git a/scene.h b/scene.h
index 38911986f8..feb91c7483 100644
--- a/scene.h
+++ b/scene.h
@@ -124,7 +124,7 @@ protected:
// shared implementation, starts painting the window
virtual void paintWindow(Window* w, int mask, QRegion region, WindowQuadList quads);
// called after all effects had their drawWindow() called
- void finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data);
+ virtual void finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data);
// compute time since the last repaint
void updateTimeDiff();
// saved data for 2nd pass of optimized screen painting
diff --git a/scene_opengl.cpp b/scene_opengl.cpp
index cbcc0cabdd..fe8bc9793c 100644
--- a/scene_opengl.cpp
+++ b/scene_opengl.cpp
@@ -70,12 +70,14 @@ Sources and other compositing managers:
#include
+#include
#include
#include "utils.h"
#include "client.h"
#include "deleted.h"
#include "effects.h"
+#include "lanczosfilter.h"
#include "overlaywindow.h"
#include
@@ -87,6 +89,7 @@ Sources and other compositing managers:
#include
#include
+#include
#include
#include
#include
@@ -205,6 +208,38 @@ void SceneOpenGL::paintBackground(QRegion region)
}
}
+void SceneOpenGL::finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data)
+{
+ if (options->isGlColorCorrection()) {
+ // Split the painting for separate screens
+ int numScreens = Workspace::self()->numScreens();
+ for (int screen = 0; screen < numScreens; ++ screen) {
+ QRegion regionForScreen(region);
+ if (numScreens > 1)
+ regionForScreen = region.intersected(Workspace::self()->screenGeometry(screen));
+
+ data.setScreen(screen);
+ performPaint(w, mask, regionForScreen, data);
+ }
+ } else {
+ performPaint(w, mask, region, data);
+ }
+}
+
+void SceneOpenGL::performPaint(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data)
+{
+ if (mask & PAINT_WINDOW_LANCZOS) {
+ if (lanczos_filter.isNull()) {
+ lanczos_filter = new LanczosFilter(this);
+ // recreate the lanczos filter when the screen gets resized
+ connect(QApplication::desktop(), SIGNAL(screenCountChanged(int)), lanczos_filter.data(), SLOT(deleteLater()));
+ connect(QApplication::desktop(), SIGNAL(resized(int)), lanczos_filter.data(), SLOT(deleteLater()));
+ }
+ lanczos_filter.data()->performPaint(w, mask, region, data);
+ } else
+ w->sceneWindow()->performPaint(mask, region, data);
+}
+
void SceneOpenGL::windowAdded(Toplevel* c)
{
assert(!windows.contains(c));
@@ -489,6 +524,8 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData
data.shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
data.shader->setUniform(GLShader::Offset, QVector2D(x(), y()));
}
+ if (options->isGlColorCorrection())
+ ColorCorrection::instance()->setupForOutput(data.screen());
sceneShader = true;
}
@@ -573,9 +610,9 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData
WindowQuadList contentQuads = data.quads.select(WindowQuadContents);
if (!contentQuads.empty()) {
texture.bind();
- prepareStates(Content, data.opacity(), data.brightness(), data.saturation(), data.shader);
+ prepareStates(Content, data.opacity(), data.brightness(), data.saturation(), data.screen(), data.shader);
renderQuads(mask, region, contentQuads, &texture, false, hardwareClipping);
- restoreStates(Content, data.opacity(), data.brightness(), data.saturation(), data.shader);
+ restoreStates(Content, data.opacity(), data.brightness(), data.saturation(), data.screen(), data.shader);
texture.unbind();
#ifndef KWIN_HAVE_OPENGLES
if (static_cast(scene)->debug) {
@@ -646,10 +683,10 @@ void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType
decorationTexture->setWrapMode(GL_CLAMP_TO_EDGE);
decorationTexture->bind();
- prepareStates(decorationType, data.opacity() * data.decorationOpacity(), data.brightness(), data.saturation(), data.shader);
+ prepareStates(decorationType, data.opacity() * data.decorationOpacity(), data.brightness(), data.saturation(), data.screen(), data.shader);
makeDecorationArrays(quads, rect, decorationTexture);
GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES, hardwareClipping);
- restoreStates(decorationType, data.opacity() * data.decorationOpacity(), data.brightness(), data.saturation(), data.shader);
+ restoreStates(decorationType, data.opacity() * data.decorationOpacity(), data.brightness(), data.saturation(), data.screen(), data.shader);
decorationTexture->unbind();
#ifndef KWIN_HAVE_OPENGLES
if (static_cast(scene)->debug) {
@@ -683,9 +720,9 @@ void SceneOpenGL::Window::paintShadow(const QRegion ®ion, const WindowPaintDa
texture->setFilter(GL_NEAREST);
texture->setWrapMode(GL_CLAMP_TO_EDGE);
texture->bind();
- prepareStates(Shadow, data.opacity(), data.brightness(), data.saturation(), data.shader, texture);
+ prepareStates(Shadow, data.opacity(), data.brightness(), data.saturation(), data.screen(), data.shader, texture);
renderQuads(0, region, quads, texture, true, hardwareClipping);
- restoreStates(Shadow, data.opacity(), data.brightness(), data.saturation(), data.shader, texture);
+ restoreStates(Shadow, data.opacity(), data.brightness(), data.saturation(), data.screen(), data.shader, texture);
texture->unbind();
#ifndef KWIN_HAVE_OPENGLES
if (static_cast(scene)->debug) {
@@ -781,10 +818,10 @@ void SceneOpenGL::Window::renderQuads(int, const QRegion& region, const WindowQu
delete[] texcoords;
}
-void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader)
+void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader)
{
if (shader)
- prepareShaderRenderStates(type, opacity, brightness, saturation, shader);
+ prepareShaderRenderStates(type, opacity, brightness, saturation, screen, shader);
else {
Texture *tex = NULL;
switch(type) {
@@ -806,20 +843,20 @@ void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double
default:
return;
}
- prepareStates(type, opacity, brightness, saturation, shader, tex);
+ prepareStates(type, opacity, brightness, saturation, screen, shader, tex);
}
}
-void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture)
+void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader, GLTexture *texture)
{
if (shader) {
- prepareShaderRenderStates(type, opacity, brightness, saturation, shader);
+ prepareShaderRenderStates(type, opacity, brightness, saturation, screen, shader);
} else {
- prepareRenderStates(type, opacity, brightness, saturation, texture);
+ prepareRenderStates(type, opacity, brightness, saturation, screen, texture);
}
}
-void SceneOpenGL::Window::prepareShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader)
+void SceneOpenGL::Window::prepareShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader)
{
// setup blending of transparent windows
bool opaque = isOpaque() && opacity == 1.0;
@@ -828,12 +865,15 @@ void SceneOpenGL::Window::prepareShaderRenderStates(TextureType type, double opa
opaque = false;
if (!opaque) {
glEnable(GL_BLEND);
- if (alpha) {
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glBlendColor((float)opacity, (float)opacity, (float)opacity, (float)opacity);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA);
- }
+ if (!options->isGlColorCorrection()) {
+ if (alpha) {
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ } else {
+ glBlendColor((float)opacity, (float)opacity, (float)opacity, (float)opacity);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA);
+ }
+ } else
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
const float rgb = brightness * opacity;
@@ -842,10 +882,14 @@ void SceneOpenGL::Window::prepareShaderRenderStates(TextureType type, double opa
shader->setUniform(GLShader::ModulationConstant, QVector4D(rgb, rgb, rgb, a));
shader->setUniform(GLShader::Saturation, saturation);
shader->setUniform(GLShader::AlphaToOne, opaque ? 1 : 0);
+
+ if (options->isGlColorCorrection())
+ ColorCorrection::instance()->setupForOutput(screen);
}
-void SceneOpenGL::Window::prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex)
+void SceneOpenGL::Window::prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLTexture *tex)
{
+ Q_UNUSED(screen)
#ifdef KWIN_HAVE_OPENGLES
Q_UNUSED(type)
Q_UNUSED(opacity)
@@ -980,10 +1024,10 @@ void SceneOpenGL::Window::prepareRenderStates(TextureType type, double opacity,
#endif
}
-void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader)
+void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader)
{
if (shader)
- restoreShaderRenderStates(type, opacity, brightness, saturation, shader);
+ restoreShaderRenderStates(type, opacity, brightness, saturation, screen, shader);
else {
Texture *tex = NULL;
switch(type) {
@@ -1005,23 +1049,24 @@ void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double
default:
return;
}
- restoreStates(type, opacity, brightness, saturation, shader, tex);
+ restoreStates(type, opacity, brightness, saturation, screen, shader, tex);
}
}
-void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture)
+void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader, GLTexture *texture)
{
if (shader) {
- restoreShaderRenderStates(type, opacity, brightness, saturation, shader);
+ restoreShaderRenderStates(type, opacity, brightness, saturation, screen, shader);
} else {
- restoreRenderStates(type, opacity, brightness, saturation, texture);
+ restoreRenderStates(type, opacity, brightness, saturation, screen, texture);
}
}
-void SceneOpenGL::Window::restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader)
+void SceneOpenGL::Window::restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader)
{
Q_UNUSED(brightness);
Q_UNUSED(saturation);
+ Q_UNUSED(screen)
Q_UNUSED(shader);
bool opaque = isOpaque() && opacity == 1.0;
if (type != Content)
@@ -1032,9 +1077,10 @@ void SceneOpenGL::Window::restoreShaderRenderStates(TextureType type, double opa
ShaderManager::instance()->getBoundShader()->setUniform(GLShader::AlphaToOne, 0);
}
-void SceneOpenGL::Window::restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex)
+void SceneOpenGL::Window::restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLTexture *tex)
{
Q_UNUSED(type)
+ Q_UNUSED(screen)
#ifdef KWIN_HAVE_OPENGLES
Q_UNUSED(opacity)
Q_UNUSED(brightness)
diff --git a/scene_opengl.h b/scene_opengl.h
index de33ce4d00..68406b2766 100644
--- a/scene_opengl.h
+++ b/scene_opengl.h
@@ -56,6 +56,8 @@ public:
protected:
virtual void paintGenericScreen(int mask, ScreenPaintData data);
virtual void paintBackground(QRegion region);
+ virtual void finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data);
+ virtual void performPaint(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data);
QMatrix4x4 transformation(int mask, const ScreenPaintData &data) const;
public Q_SLOTS:
virtual void windowOpacityChanged(KWin::Toplevel* c);
@@ -184,14 +186,14 @@ protected:
void paintShadow(const QRegion ®ion, const WindowPaintData &data, bool hardwareClipping);
void makeDecorationArrays(const WindowQuadList& quads, const QRect &rect, Texture *tex) const;
void renderQuads(int, const QRegion& region, const WindowQuadList& quads, GLTexture* tex, bool normalized, bool hardwareClipping);
- void prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader);
- void prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture);
- void prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex);
- void prepareShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader);
- void restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader);
- void restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture);
- void restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex);
- void restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader);
+ void prepareStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader);
+ void prepareStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader, GLTexture *texture);
+ void prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLTexture *tex);
+ void prepareShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader);
+ void restoreStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader);
+ void restoreStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader, GLTexture *texture);
+ void restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLTexture *tex);
+ void restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, int screen, GLShader* shader);
private:
Texture texture;
diff --git a/workspace.cpp b/workspace.cpp
index ecf0d76cfc..148b60d73a 100644
--- a/workspace.cpp
+++ b/workspace.cpp
@@ -191,6 +191,7 @@ Workspace::Workspace(bool restore)
connect(&rulesUpdatedTimer, SIGNAL(timeout()), this, SLOT(writeWindowRules()));
connect(&unredirectTimer, SIGNAL(timeout()), this, SLOT(delayedCheckUnredirect()));
connect(&compositeResetTimer, SIGNAL(timeout()), this, SLOT(resetCompositing()));
+ connect(options, SIGNAL(glColorCorrectionChanged()), this, SLOT(resetCompositing()));
unredirectTimer.setSingleShot(true);
compositeResetTimer.setSingleShot(true);