diff --git a/kcmkwin/kwincompositing/main.cpp b/kcmkwin/kwincompositing/main.cpp index fdf934b273..84a2895788 100644 --- a/kcmkwin/kwincompositing/main.cpp +++ b/kcmkwin/kwincompositing/main.cpp @@ -130,6 +130,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())); connect(ui.ghns, SIGNAL(clicked(bool)), SLOT(slotGHNS())); @@ -384,6 +385,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()); } @@ -517,7 +519,7 @@ bool KWinCompositingConfig::saveAdvancedTab() config.writeEntry("GLVSync", ui.glVSync->isChecked()); config.writeEntry("GLLegacy", !ui.glShaders->isChecked()); - + config.writeEntry("GLColorCorrection", ui.glColorCorrection->isChecked()); return advancedChanged; } @@ -755,6 +757,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 339efde78e..d4e99487dd 100644 --- a/kcmkwin/kwincompositing/main.ui +++ b/kcmkwin/kwincompositing/main.ui @@ -806,6 +806,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 + + + @@ -816,14 +827,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. + <p>Activates color correction if possible, using the Kolor-Manager. Requires OpenGL 2 Shaders to be enabled and Kolor-Manager to be installed. May fail silently.</p><p><strong>Experimental</strong>.</p> - Use OpenGL 2 Shaders + Enable color correction (experimental) @@ -921,12 +934,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 065b892e76..d10a3e72f3 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 023a452988..4895ca0e1e 100644 --- a/libkwineffects/kwineffects.cpp +++ b/libkwineffects/kwineffects.cpp @@ -211,6 +211,7 @@ public: qreal decorationOpacity; qreal saturation; qreal brightness; + int 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; } +int 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(int screen) const +{ + d->screen = screen; +} + qreal WindowPaintData::multiplyDecorationOpacity(qreal factor) { d->decorationOpacity *= factor; diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h index 1cae746b64..32bcf501bd 100644 --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -1949,6 +1949,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 + */ + int screen() const; + /** + * @param screen New screen number + * A value less than 0 will indicate that a default profile should be done. + */ + void setScreen(int 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..b5127e6ed9 --- /dev/null +++ b/libkwineffects/kwinglcolorcorrection.cpp @@ -0,0 +1,629 @@ +/******************************************************************** + 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 "kwinglplatform.h" +#include "kwinglutils.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef KWIN_HAVE_OPENGLES +#define CC_TEXTURE_INTERNAL_FORMAT GL_RGB + +/* + * A bit of ugliness to allow building with OpenGL ES < 3, without + * ifdef's everywhere in the code. These should not actually be used anywhere. + */ +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F // From OES_texture_3D extension +#define GL_TEXTURE_WRAP_R 0x8072 // From OES_texture_3D extension +void glTexImage3D(GLenum, int, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void *) +{ + Q_ASSERT(false); // Execution must not reach here +} +#endif // defined(GL_TEXTURE_3D) + +#else // KWIN_HAVE_OPENGLES +#define CC_TEXTURE_INTERNAL_FORMAT GL_RGB16 +#endif // KWIN_HAVE_OPENGLES + + +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 / gl_FragColor.a).rgb;\n"; + + +/* + * Color Correction + */ + +ColorCorrection::ColorCorrection(QObject *parent) + : QObject(parent) + , 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(); + + 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; + } + + const GLPlatform *gl = GLPlatform::instance(); + if (enabled && gl->isGLES() && (gl->glVersion() >> 32) < 3) { + kError(1212) << "color correction is not supported with OpenGL ES < 3.0"; + return; + } + + if (enabled) { + // Update all profiles and regions + d->m_csi->update(); + } else { + d->deleteCCTextures(); + } + + d->m_enabled = enabled; + GLShader::sColorCorrect = enabled; + kDebug(1212) << enabled; +} + +void ColorCorrection::setupForOutput(int screen) +{ + Q_D(ColorCorrection); + + 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)) { + // This means the color correction shaders are probably not loaded + if (!d->m_enabled) + return; + kError(1212) << "unable to set uniform for the color correction lookup texture"; + } + + d->setupCCTextures(); + + GLint activeTexture; + glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture); + glActiveTexture(GL_TEXTURE0 + d->m_ccTextureUnit); + + if (d->m_outputCCTextures.isEmpty() || 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); + + checkGLError("setupForOutput"); + + d->m_lastOutput = screen; +} + +void ColorCorrection::reset() +{ + setupForOutput(-1); +} + +QByteArray ColorCorrection::prepareFragmentShader(const QByteArray &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() +{ + if (m_ccTextureUnit < 0) { + GLint maxUnits = 0; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxUnits); + 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)); + } + + // TODO Handle errors (what if a texture isn't generated?) + checkGLError("setupCCTextures"); +} + +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(); + } + + checkGLError("deleteCCTextures"); +} + +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; + + glBindTexture(GL_TEXTURE_3D, texture); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + 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, CC_TEXTURE_INTERNAL_FORMAT, + LUT_GRID_POINTS, LUT_GRID_POINTS, LUT_GRID_POINTS, + 0, GL_RGB, GL_UNSIGNED_SHORT, clut.data()); + + + checkGLError("setupCCTexture"); +} + +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..ec080c0239 --- /dev/null +++ b/libkwineffects/kwinglcolorcorrection.h @@ -0,0 +1,95 @@ +/******************************************************************** + 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: + explicit ColorCorrection(QObject *parent = 0); + virtual ~ColorCorrection(); + + /** + * Prepares color correction for the output number \param screen. + * Sets up the appropriate color lookup texture for the output. + */ + void setupForOutput(int screen); + + /** + * 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(); + + /** + * 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. + */ + static 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: + ColorCorrectionPrivate * const d_ptr; + Q_DECLARE_PRIVATE(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..65269df188 --- /dev/null +++ b/libkwineffects/kwinglcolorcorrection_p.h @@ -0,0 +1,165 @@ +/******************************************************************** + 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; + 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/kwinglplatform.cpp b/libkwineffects/kwinglplatform.cpp index 616744dbd8..c578967955 100644 --- a/libkwineffects/kwinglplatform.cpp +++ b/libkwineffects/kwinglplatform.cpp @@ -1031,5 +1031,14 @@ CompositingType GLPlatform::recommendedCompositor() const return m_recommendedCompositor; } +bool GLPlatform::isGLES() const +{ +#ifdef KWIN_HAVE_OPENGLES + return true; +#else + return false; +#endif +} + } // namespace KWin diff --git a/libkwineffects/kwinglplatform.h b/libkwineffects/kwinglplatform.h index 249ecdb55e..35d05829ae 100644 --- a/libkwineffects/kwinglplatform.h +++ b/libkwineffects/kwinglplatform.h @@ -295,6 +295,10 @@ public: * @since 4.9 **/ bool isLooseBinding() const; + /** + * @returns Whether OpenGL ES is used + */ + bool isGLES() const; /** * @returns The CompositingType recommended by the driver. diff --git a/libkwineffects/kwinglutils.cpp b/libkwineffects/kwinglutils.cpp index 3301b34ffc..53d1fb502f 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" @@ -258,6 +259,8 @@ void popMatrix() // GLShader //**************************************** +bool GLShader::sColorCorrect = false; + GLShader::GLShader() : mProgram(0) , mValid(false) @@ -299,10 +302,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 @@ -313,7 +314,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 && sColorCorrect) + ba = ColorCorrection::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 f0237a33c1..ab82068a3b 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(); @@ -219,6 +220,9 @@ private: int mFloatLocation[FloatUniformCount]; int mIntLocation[IntUniformCount]; + static bool sColorCorrect; + + friend class ColorCorrection; friend class ShaderManager; }; diff --git a/options.cpp b/options.cpp index 6a3a0250cc..a49ff922c2 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_colorCorrected(Options::defaultColorCorrected()) , m_xrenderSmoothScale(Options::defaultXrenderSmoothScale()) , m_maxFpsInterval(Options::defaultMaxFpsInterval()) , m_refreshRate(Options::defaultRefreshRate()) @@ -719,6 +720,15 @@ void Options::setGlVSync(bool glVSync) emit glVSyncChanged(); } +void Options::setColorCorrected(bool colorCorrected) +{ + if (m_colorCorrected == colorCorrected) { + return; + } + m_colorCorrected = colorCorrected; + emit colorCorrectedChanged(); +} + void Options::setXrenderSmoothScale(bool xrenderSmoothScale) { if (m_xrenderSmoothScale == xrenderSmoothScale) { @@ -1020,6 +1030,8 @@ void Options::reloadCompositingSettings(bool force) } setGlLegacy(config.readEntry("GLLegacy", Options::defaultGlLegacy())); + setColorCorrected(config.readEntry("GLColorCorrection", Options::defaultColorCorrected())); + m_xrenderSmoothScale = config.readEntry("XRenderSmoothScale", false); HiddenPreviews previews = Options::defaultHiddenPreviews(); diff --git a/options.h b/options.h index 2a5ab65624..f9f623f346 100644 --- a/options.h +++ b/options.h @@ -187,6 +187,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 colorCorrected READ isColorCorrected WRITE setColorCorrected NOTIFY colorCorrectedChanged) 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) @@ -561,6 +562,9 @@ public: bool isGlVSync() const { return m_glVSync; } + bool isColorCorrected() const { + return m_colorCorrected; + } // XRender bool isXrenderSmoothScale() const { return m_xrenderSmoothScale; @@ -645,6 +649,7 @@ public: void setUnredirectFullscreen(bool unredirectFullscreen); void setGlSmoothScale(int glSmoothScale); void setGlVSync(bool glVSync); + void setColorCorrected(bool colorCorrected); void setXrenderSmoothScale(bool xrenderSmoothScale); void setMaxFpsInterval(uint maxFpsInterval); void setRefreshRate(uint refreshRate); @@ -854,6 +859,9 @@ public: static bool defaultGlVSync() { return true; } + static bool defaultColorCorrected() { + return false; + } static bool defaultXrenderSmoothScale() { return false; } @@ -956,6 +964,7 @@ Q_SIGNALS: void unredirectFullscreenChanged(); void glSmoothScaleChanged(); void glVSyncChanged(); + void colorCorrectedChanged(); void xrenderSmoothScaleChanged(); void maxFpsIntervalChanged(); void refreshRateChanged(); @@ -999,6 +1008,7 @@ private: bool m_unredirectFullscreen; int m_glSmoothScale; bool m_glVSync; + bool m_colorCorrected; bool m_xrenderSmoothScale; uint m_maxFpsInterval; // Settings that should be auto-detected diff --git a/scene.cpp b/scene.cpp index 6581a05a26..2e4190afac 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" @@ -454,16 +452,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); } bool Scene::waitSyncAvailable() const diff --git a/scene.h b/scene.h index be3acc497e..1b17066018 100644 --- a/scene.h +++ b/scene.h @@ -32,7 +32,6 @@ class Workspace; class Deleted; class EffectFrameImpl; class EffectWindowImpl; -class LanczosFilter; class OverlayWindow; class Shadow; @@ -125,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 @@ -154,7 +153,6 @@ protected: int time_diff; QElapsedTimer last_time; Workspace* wspace; - QWeakPointer lanczos_filter; }; // The base class for windows representations in composite backends diff --git a/scene_opengl.cpp b/scene_opengl.cpp index d8f9c62764..56af4f8b42 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -76,6 +76,7 @@ Sources and other compositing managers: #include +#include #include #include "utils.h" @@ -83,6 +84,7 @@ Sources and other compositing managers: #include "composite.h" #include "deleted.h" #include "effects.h" +#include "lanczosfilter.h" #include "overlaywindow.h" #include "paintredirector.h" @@ -95,6 +97,7 @@ Sources and other compositing managers: #include #include +#include #include #include #include @@ -467,7 +470,14 @@ bool SceneOpenGL2::supported(OpenGLBackend *backend) SceneOpenGL2::SceneOpenGL2(OpenGLBackend *backend) : SceneOpenGL(Workspace::self(), backend) + , m_colorCorrection(new ColorCorrection(this)) { + // Initialize color correction before the shaders + kDebug(1212) << "Color correction:" << options->isColorCorrected(); + m_colorCorrection->setEnabled(options->isColorCorrected()); + connect(m_colorCorrection, SIGNAL(changed()), Compositor::self(), SLOT(addRepaintFull())); + connect(options, SIGNAL(colorCorrectedChanged()), this, SLOT(slotColorCorrectedChanged())); + if (!ShaderManager::instance()->isValid()) { kDebug(1212) << "No Scene Shaders available"; return; @@ -515,6 +525,52 @@ SceneOpenGL::Window *SceneOpenGL2::createWindow(Toplevel *t) return new SceneOpenGL2Window(t); } +void SceneOpenGL2::finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data) +{ + if (options->isColorCorrected()) { + // 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); + performPaintWindow(w, mask, regionForScreen, data); + } + } else { + performPaintWindow(w, mask, region, data); + } +} + +void SceneOpenGL2::performPaintWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data) +{ + if (mask & PAINT_WINDOW_LANCZOS) { + if (m_lanczosFilter.isNull()) { + m_lanczosFilter = new LanczosFilter(this); + // recreate the lanczos filter when the screen gets resized + connect(QApplication::desktop(), SIGNAL(screenCountChanged(int)), m_lanczosFilter.data(), SLOT(deleteLater())); + connect(QApplication::desktop(), SIGNAL(resized(int)), m_lanczosFilter.data(), SLOT(deleteLater())); + } + m_lanczosFilter.data()->performPaint(w, mask, region, data); + } else + w->sceneWindow()->performPaint(mask, region, data); +} + +ColorCorrection *SceneOpenGL2::colorCorrection() +{ + return m_colorCorrection; +} + +void SceneOpenGL2::slotColorCorrectedChanged() +{ + m_colorCorrection->setEnabled(options->isColorCorrected()); + + // Reload all shaders + ShaderManager::cleanup(); + ShaderManager::instance(); +} + //**************************************** // SceneOpenGL1 //**************************************** @@ -711,12 +767,12 @@ SceneOpenGL::TexturePrivate::~TexturePrivate() SceneOpenGL::Window::Window(Toplevel* c) : Scene::Window(c) + , m_scene(NULL) , texture(NULL) , topTexture(NULL) , leftTexture(NULL) , rightTexture(NULL) , bottomTexture(NULL) - , m_scene(NULL) { } @@ -895,7 +951,7 @@ 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()); + prepareStates(Content, data.opacity(), data.brightness(), data.saturation(), data.screen()); renderQuads(mask, region, contentQuads, texture, false, hardwareClipping); restoreStates(Content, data.opacity(), data.brightness(), data.saturation()); texture->unbind(); @@ -1025,7 +1081,7 @@ 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()); + prepareStates(decorationType, data.opacity() * data.decorationOpacity(), data.brightness(), data.saturation(), data.screen()); makeDecorationArrays(quads, rect, decorationTexture); GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES, hardwareClipping); restoreStates(decorationType, data.opacity() * data.decorationOpacity(), data.brightness(), data.saturation()); @@ -1062,7 +1118,7 @@ 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()); + prepareStates(Shadow, data.opacity(), data.brightness(), data.saturation(), data.screen()); renderQuads(0, region, quads, texture, true, hardwareClipping); restoreStates(Shadow, data.opacity(), data.brightness(), data.saturation()); texture->unbind(); @@ -1213,6 +1269,8 @@ void SceneOpenGL2Window::beginRenderWindow(int mask, const WindowPaintData &data } shader->setUniform(GLShader::WindowTransformation, transformation(mask, data)); + + static_cast(m_scene)->colorCorrection()->setupForOutput(data.screen()); } void SceneOpenGL2Window::endRenderWindow(const WindowPaintData &data) @@ -1222,7 +1280,7 @@ void SceneOpenGL2Window::endRenderWindow(const WindowPaintData &data) } } -void SceneOpenGL2Window::prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation) +void SceneOpenGL2Window::prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen) { // setup blending of transparent windows bool opaque = isOpaque() && opacity == 1.0; @@ -1241,11 +1299,15 @@ void SceneOpenGL2Window::prepareStates(TextureType type, qreal opacity, qreal br } if (!opaque) { glEnable(GL_BLEND); - if (alpha) { - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + if (options->isColorCorrected()) { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { - glBlendColor((float)opacity, (float)opacity, (float)opacity, (float)opacity); - glBlendFunc(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA); + 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); + } } } m_blendingEnabled = !opaque; @@ -1256,6 +1318,8 @@ void SceneOpenGL2Window::prepareStates(TextureType type, qreal opacity, qreal br GLShader *shader = ShaderManager::instance()->getBoundShader(); shader->setUniform(GLShader::ModulationConstant, QVector4D(rgb, rgb, rgb, a)); shader->setUniform(GLShader::Saturation, saturation); + + static_cast(m_scene)->colorCorrection()->setupForOutput(screen); } void SceneOpenGL2Window::restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation) @@ -1264,10 +1328,11 @@ void SceneOpenGL2Window::restoreStates(TextureType type, qreal opacity, qreal br Q_UNUSED(opacity); Q_UNUSED(brightness); Q_UNUSED(saturation); - if (m_blendingEnabled) { glDisable(GL_BLEND); } + + static_cast(m_scene)->colorCorrection()->setupForOutput(-1); } //*************************************** @@ -1294,8 +1359,10 @@ void SceneOpenGL1Window::endRenderWindow(const WindowPaintData &data) popMatrix(); } -void SceneOpenGL1Window::prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation) +void SceneOpenGL1Window::prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen) { + Q_UNUSED(screen) + GLTexture *tex = textureForType(type); bool alpha = false; bool opaque = true; diff --git a/scene_opengl.h b/scene_opengl.h index 990800e700..e6142c0c2c 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -30,6 +30,8 @@ along with this program. If not, see . namespace KWin { +class ColorCorrection; +class LanczosFilter; class OpenGLBackend; class SceneOpenGL @@ -84,6 +86,7 @@ private: class SceneOpenGL2 : public SceneOpenGL { + Q_OBJECT public: SceneOpenGL2(OpenGLBackend *backend); virtual ~SceneOpenGL2(); @@ -93,10 +96,23 @@ public: static bool supported(OpenGLBackend *backend); + ColorCorrection *colorCorrection(); + protected: virtual void paintGenericScreen(int mask, ScreenPaintData data); virtual void doPaintBackground(const QVector< float >& vertices); virtual SceneOpenGL::Window *createWindow(Toplevel *t); + virtual void finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data); + +private Q_SLOTS: + void slotColorCorrectedChanged(); + +private: + void performPaintWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data); + +private: + QWeakPointer m_lanczosFilter; + ColorCorrection *m_colorCorrection; }; #ifdef KWIN_HAVE_OPENGL_1 @@ -226,8 +242,9 @@ protected: * @param opacity The opacity value to use for this rendering * @param brightness The brightness value to use for this rendering * @param saturation The saturation value to use for this rendering + * @param screen The index of the screen to use for this rendering **/ - virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation) = 0; + virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen) = 0; /** * @brief Restores the OpenGL rendering state after the texture with @p type has been rendered. * @@ -235,6 +252,7 @@ protected: * @param opacity The opacity value used for the rendering * @param brightness The brightness value used for this rendering * @param saturation The saturation value used for this rendering + * @param screen The index of the screen to use for this rendering **/ virtual void restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation) = 0; @@ -246,6 +264,9 @@ protected: **/ GLTexture *textureForType(TextureType type); +protected: + SceneOpenGL *m_scene; + private: template void paintDecorations(const WindowPaintData &data, const QRegion ®ion, bool hardwareClipping); @@ -254,7 +275,6 @@ private: Texture *leftTexture; Texture *rightTexture; Texture *bottomTexture; - SceneOpenGL *m_scene; }; class SceneOpenGL2Window : public SceneOpenGL::Window @@ -266,7 +286,7 @@ public: protected: virtual void beginRenderWindow(int mask, const WindowPaintData &data); virtual void endRenderWindow(const WindowPaintData &data); - virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation); + virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen); virtual void restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation); private: @@ -286,7 +306,7 @@ public: protected: virtual void beginRenderWindow(int mask, const WindowPaintData &data); virtual void endRenderWindow(const WindowPaintData &data); - virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation); + virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen); virtual void restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation); }; #endif