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);