From 01adbe6dc536905606db498137a26518d670b757 Mon Sep 17 00:00:00 2001 From: Casian Andrei Date: Tue, 13 Nov 2012 22:41:02 +0200 Subject: [PATCH] Implement color correction (per output) Add an option to kcmcompositing in the 'Advanced' tab, to enable or disable color correction. It is specified that it's experimental and it needs Kolor Manager. Before painting for a particular screen, ColorCorrection::setupForOutput should be called. A screen property is added for WindowPaintData. In kwinglutils, The fragment shaders are intercepted before being compiled and they get a couple of lines of code inserted in order to do the color correction. This happens only when color correction is enabled, of course. For D-Bus communication with KolorServer, everything is async. The implementation basically manages a set of color lookup tables for different outputs and for different window regions. These are taken via D-Bus. Each lookup table has around 700 KB. This commit reintroduces the changes from the former merge with the "color2" branch. In this form, it can be easily reverted. REVIEW: 106141 --- kcmkwin/kwincompositing/main.cpp | 5 +- kcmkwin/kwincompositing/main.ui | 47 +- libkwineffects/CMakeLists.txt | 1 + libkwineffects/kwineffects.cpp | 13 + libkwineffects/kwineffects.h | 12 + libkwineffects/kwinglcolorcorrection.cpp | 629 +++++++++++++++++++++++ libkwineffects/kwinglcolorcorrection.h | 95 ++++ libkwineffects/kwinglcolorcorrection_p.h | 165 ++++++ libkwineffects/kwinglplatform.cpp | 9 + libkwineffects/kwinglplatform.h | 4 + libkwineffects/kwinglutils.cpp | 21 +- libkwineffects/kwinglutils.h | 4 + options.cpp | 12 + options.h | 10 + scene.cpp | 13 +- scene.h | 4 +- scene_opengl.cpp | 89 +++- scene_opengl.h | 28 +- 18 files changed, 1117 insertions(+), 44 deletions(-) create mode 100644 libkwineffects/kwinglcolorcorrection.cpp create mode 100644 libkwineffects/kwinglcolorcorrection.h create mode 100644 libkwineffects/kwinglcolorcorrection_p.h 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