diff --git a/effects/logout/logout.cpp b/effects/logout/logout.cpp index 59c078bec..34fa81d02 100644 --- a/effects/logout/logout.cpp +++ b/effects/logout/logout.cpp @@ -1,389 +1,392 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Lubos Lunak Copyright (C) 2009 Martin Gräßlin Copyright (C) 2009, 2010 Lucas Murray 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 "logout.h" // KConfigSkeleton #include "logoutconfig.h" #include "kwinglutils.h" #include "kwinglplatform.h" #include #include #include namespace KWin { LogoutEffect::LogoutEffect() : progress(0.0) , displayEffect(false) , logoutWindow(NULL) , logoutWindowClosed(true) , logoutWindowPassed(false) , logoutAtom(QByteArrayLiteral("_KDE_LOGGING_OUT")) , canDoPersistent(false) , ignoredWindows() , m_vignettingShader(NULL) , m_blurShader(NULL) , m_shadersDir(QStringLiteral("kwin/shaders/1.10/")) { if (logoutAtom.isValid()) { // Persistent effect effects->registerPropertyType(logoutAtom, true); } blurTexture = NULL; blurTarget = NULL; reconfigure(ReconfigureAll); connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long))); #ifdef KWIN_HAVE_OPENGLES const qint64 coreVersionNumber = kVersionNumber(3, 0); #else const qint64 coreVersionNumber = kVersionNumber(1, 40); #endif if (GLPlatform::instance()->glslVersion() >= coreVersionNumber) m_shadersDir = QStringLiteral("kwin/shaders/1.40/"); } LogoutEffect::~LogoutEffect() { delete blurTexture; delete blurTarget; delete m_vignettingShader; delete m_blurShader; } void LogoutEffect::reconfigure(ReconfigureFlags) { LogoutConfig::self()->read(); frameDelay = 0; useBlur = LogoutConfig::useBlur(); delete blurTexture; blurTexture = NULL; delete blurTarget; blurTarget = NULL; blurSupported = false; delete m_blurShader; m_blurShader = NULL; } void LogoutEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (!displayEffect && progress == 0.0) { if (blurTexture) { delete blurTexture; blurTexture = NULL; delete blurTarget; blurTarget = NULL; blurSupported = false; } } else if (!blurTexture) { blurSupported = false; delete blurTarget; // catch as we just tested the texture ;-P if (effects->isOpenGLCompositing() && GLRenderTarget::blitSupported() && useBlur) { // TODO: It seems that it is not possible to create a GLRenderTarget that has // a different size than the display right now. Most likely a KWin core bug. // Create texture and render target - blurTexture = new GLTexture(effects->virtualScreenSize()); + const QSize size = effects->virtualScreenSize(); + + // The fragment shader uses a LOD bias of 1.75, so we need 3 mipmap levels. + blurTexture = new GLTexture(size, 3); blurTexture->setFilter(GL_LINEAR_MIPMAP_LINEAR); blurTexture->setWrapMode(GL_CLAMP_TO_EDGE); blurTarget = new GLRenderTarget(*blurTexture); if (blurTarget->valid()) blurSupported = true; // As creating the render target takes time it can cause the first two frames of the // blur animation to be jerky. For this reason we only start the animation after the // third frame. frameDelay = 2; } } if (frameDelay) --frameDelay; else { if (displayEffect) progress = qMin(1.0, progress + time / animationTime(2000.0)); else if (progress > 0.0) progress = qMax(0.0, progress - time / animationTime(500.0)); } if (blurSupported && progress > 0.0) { data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; } data.paint |= effects->clientArea(FullArea, 0, 0); effects->prePaintScreen(data, time); } void LogoutEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (progress > 0.0) { if (effects->isOpenGLCompositing()) { // In OpenGL mode we add vignetting and, if supported, a slight blur if (blurSupported) { // When using blur we render everything to an FBO and as such don't do the vignetting // until after we render the FBO to the screen. if (w == logoutWindow) { // Window is rendered after the FBO windowOpacity = data.opacity(); data.setOpacity(0.0); // Cheat, we need the opacity for later but don't want to blur it } else { if (logoutWindowPassed || ignoredWindows.contains(w)) { // Window is rendered after the FBO windows.append(w); windowsOpacities[ w ] = data.opacity(); data.setOpacity(0.0); } else // Window is added to the FBO data.multiplySaturation((1.0 - progress * 0.2)); } } else { // If we are not blurring then we are not rendering to an FBO if (w == logoutWindow) // This is the logout window don't alter it but render our vignetting now renderVignetting(); else if (!logoutWindowPassed && !ignoredWindows.contains(w)) // Window is in the background, desaturate data.multiplySaturation((1.0 - progress * 0.2)); // All other windows are unaltered } } if (effects->compositingType() == KWin::XRenderCompositing) { // Since we can't do vignetting in XRender just do a stronger desaturation and darken if (w != logoutWindow && !logoutWindowPassed && !ignoredWindows.contains(w)) { data.multiplySaturation((1.0 - progress * 0.8)); data.multiplyBrightness((1.0 - progress * 0.3)); } } if (w == logoutWindow || ignoredWindows.contains(w)) // HACK: All windows past the first ignored one should not be // blurred as it affects the stacking order. // All following windows are on top of the logout window and should not be altered either logoutWindowPassed = true; } effects->paintWindow(w, mask, region, data); } void LogoutEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); if (effects->isOpenGLCompositing() && progress > 0.0) { if (!blurSupported) { if (!logoutWindowPassed) // The logout window has been deleted but we still want to fade out the vignetting, thus // render it on the top of everything if still animating. We don't check if logoutWindow // is set as it may still be even if it wasn't rendered. renderVignetting(); } else { GLRenderTarget::pushRenderTarget(blurTarget); blurTarget->blitFromFramebuffer(); GLRenderTarget::popRenderTarget(); //-------------------------- // Render the screen effect renderBlurTexture(); // Vignetting (Radial gradient with transparent middle and black edges) renderVignetting(); //-------------------------- // Render the logout window if (logoutWindow) { int winMask = logoutWindow->hasAlpha() ? PAINT_WINDOW_TRANSLUCENT : PAINT_WINDOW_OPAQUE; WindowPaintData winData(logoutWindow); winData.setOpacity(windowOpacity); effects->drawWindow(logoutWindow, winMask, region, winData); } // Render all windows on top of logout window foreach (EffectWindow * w, windows) { int winMask = w->hasAlpha() ? PAINT_WINDOW_TRANSLUCENT : PAINT_WINDOW_OPAQUE; WindowPaintData winData(w); winData.setOpacity(windowsOpacities[ w ]); effects->drawWindow(w, winMask, region, winData); } windows.clear(); windowsOpacities.clear(); } } } void LogoutEffect::postPaintScreen() { if ((progress != 0.0 && progress != 1.0) || frameDelay) effects->addRepaintFull(); if (progress > 0.0) logoutWindowPassed = false; effects->postPaintScreen(); } void LogoutEffect::slotWindowAdded(EffectWindow* w) { if (isLogoutDialog(w)) { logoutWindow = w; logoutWindowClosed = false; // So we don't blur the window on close progress = 0.0; displayEffect = true; ignoredWindows.clear(); effects->addRepaintFull(); } else if (canDoPersistent) // TODO: Add parent ignoredWindows.append(w); } void LogoutEffect::slotWindowClosed(EffectWindow* w) { if (w == logoutWindow) { logoutWindowClosed = true; if (!canDoPersistent) displayEffect = false; // Fade back to normal effects->addRepaintFull(); } } void LogoutEffect::slotWindowDeleted(EffectWindow* w) { windows.removeAll(w); ignoredWindows.removeAll(w); if (w == logoutWindow) logoutWindow = NULL; } bool LogoutEffect::isLogoutDialog(EffectWindow* w) { // TODO there should be probably a better way (window type?) if (w->windowClass() == QStringLiteral("ksmserver ksmserver") && (w->windowRole() == QStringLiteral("logoutdialog") || w->windowRole() == QStringLiteral("logouteffect"))) { return true; } return false; } void LogoutEffect::renderVignetting() { if (!m_vignettingShader) { m_vignettingShader = ShaderManager::instance()->loadFragmentShader(KWin::ShaderManager::ColorShader, QStandardPaths::locate(QStandardPaths::GenericDataLocation, m_shadersDir + QStringLiteral("vignetting.frag"))); if (!m_vignettingShader->isValid()) { qCDebug(KWINEFFECTS) << "Vignetting Shader failed to load"; return; } } else if (!m_vignettingShader->isValid()) { // shader broken return; } // need to get the projection matrix from the ortho shader for the vignetting shader QMatrix4x4 projection = ShaderManager::instance()->pushShader(KWin::ShaderManager::SimpleShader)->getUniformMatrix4x4("projection"); ShaderManager::instance()->popShader(); ShaderBinder binder(m_vignettingShader); m_vignettingShader->setUniform(KWin::GLShader::ProjectionMatrix, projection); m_vignettingShader->setUniform("u_progress", (float)progress * 0.9f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_SCISSOR_TEST); const QRect fullArea = effects->clientArea(FullArea, 0, 0); for (int screen = 0; screen < effects->numScreens(); screen++) { const QRect screenGeom = effects->clientArea(ScreenArea, screen, 0); glScissor(screenGeom.x(), effects->virtualScreenSize().height() - screenGeom.y() - screenGeom.height(), screenGeom.width(), screenGeom.height()); // GL coords are flipped const float cenX = screenGeom.x() + screenGeom.width() / 2; const float cenY = fullArea.height() - screenGeom.y() - screenGeom.height() / 2; const float r = float((screenGeom.width() > screenGeom.height()) ? screenGeom.width() : screenGeom.height()) * 0.8f; // Radius m_vignettingShader->setUniform("u_center", QVector2D(cenX, cenY)); m_vignettingShader->setUniform("u_radius", r); QVector vertices; vertices << screenGeom.x() << screenGeom.y(); vertices << screenGeom.x() << screenGeom.y() + screenGeom.height(); vertices << screenGeom.x() + screenGeom.width() << screenGeom.y(); vertices << screenGeom.x() + screenGeom.width() << screenGeom.y() + screenGeom.height(); GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->setData(vertices.count()/2, 2, vertices.constData(), NULL); vbo->render(GL_TRIANGLE_STRIP); } glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); } void LogoutEffect::renderBlurTexture() { if (!m_blurShader) { m_blurShader = ShaderManager::instance()->loadFragmentShader(KWin::ShaderManager::SimpleShader, QStandardPaths::locate(QStandardPaths::GenericDataLocation, m_shadersDir + QStringLiteral("logout-blur.frag"))); if (!m_blurShader->isValid()) { qCDebug(KWINEFFECTS) << "Logout blur shader failed to load"; } } else if (!m_blurShader->isValid()) { // shader is broken - no need to continue here return; } // Unmodified base image ShaderBinder binder(m_blurShader); m_blurShader->setUniform(GLShader::Offset, QVector2D(0, 0)); m_blurShader->setUniform(GLShader::ModulationConstant, QVector4D(1.0, 1.0, 1.0, 1.0)); m_blurShader->setUniform(GLShader::Saturation, 1.0); m_blurShader->setUniform("u_alphaProgress", (float)progress * 0.4f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); blurTexture->bind(); blurTexture->generateMipmaps(); blurTexture->render(infiniteRegion(), effects->virtualScreenGeometry()); blurTexture->unbind(); glDisable(GL_BLEND); } void LogoutEffect::slotPropertyNotify(EffectWindow* w, long a) { if (w || a != logoutAtom) return; // Not our atom QByteArray byteData = effects->readRootProperty(logoutAtom, logoutAtom, 8); if (byteData.length() < 1) { // Property was deleted displayEffect = false; return; } // We are using a compatible KSMServer therefore only terminate the effect when the // atom is deleted, not when the dialog is closed. canDoPersistent = true; effects->addRepaintFull(); } bool LogoutEffect::isActive() const { return progress != 0 || logoutWindow; } } // namespace diff --git a/effects/lookingglass/lookingglass.cpp b/effects/lookingglass/lookingglass.cpp index 784b303a6..e6b5c8331 100644 --- a/effects/lookingglass/lookingglass.cpp +++ b/effects/lookingglass/lookingglass.cpp @@ -1,261 +1,264 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2007 Christian Nitschkowski 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 "lookingglass.h" // KConfigSkeleton #include "lookingglassconfig.h" #include #include #include #include #include #include #include #include +#include + namespace KWin { LookingGlassEffect::LookingGlassEffect() : zoom(1.0f) , target_zoom(1.0f) , polling(false) , m_texture(NULL) , m_fbo(NULL) , m_vbo(NULL) , m_shader(NULL) , m_enabled(false) , m_valid(false) { QAction* a; a = KStandardAction::zoomIn(this, SLOT(zoomIn()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Equal); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Equal); effects->registerGlobalShortcut(Qt::META + Qt::Key_Equal, a); a = KStandardAction::zoomOut(this, SLOT(zoomOut()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Minus); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Minus); effects->registerGlobalShortcut(Qt::META + Qt::Key_Minus, a); a = KStandardAction::actualSize(this, SLOT(toggle()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_0); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_0); effects->registerGlobalShortcut(Qt::META + Qt::Key_0, a); connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); reconfigure(ReconfigureAll); } LookingGlassEffect::~LookingGlassEffect() { delete m_texture; delete m_fbo; delete m_shader; delete m_vbo; } bool LookingGlassEffect::supported() { return effects->compositingType() == OpenGL2Compositing; } void LookingGlassEffect::reconfigure(ReconfigureFlags) { LookingGlassConfig::self()->read(); initialradius = LookingGlassConfig::radius(); radius = initialradius; qCDebug(KWINEFFECTS) << QStringLiteral("Radius from config: %1").arg(radius) << endl; m_valid = loadData(); } bool LookingGlassEffect::loadData() { const QSize screenSize = effects->virtualScreenSize(); int texw = screenSize.width(); int texh = screenSize.height(); // Create texture and render target - m_texture = new GLTexture(texw, texh); + const int levels = std::log2(qMin(texw, texh)) + 1; + m_texture = new GLTexture(texw, texh, levels); m_texture->setFilter(GL_LINEAR_MIPMAP_LINEAR); m_texture->setWrapMode(GL_CLAMP_TO_EDGE); m_fbo = new GLRenderTarget(*m_texture); if (!m_fbo->valid()) { return false; } QString shadersDir = QStringLiteral("kwin/shaders/1.10/"); #ifdef KWIN_HAVE_OPENGLES const qint64 coreVersionNumber = kVersionNumber(3, 0); #else const qint64 coreVersionNumber = kVersionNumber(1, 40); #endif if (GLPlatform::instance()->glslVersion() >= coreVersionNumber) shadersDir = QStringLiteral("kwin/shaders/1.40/"); const QString fragmentshader = QStandardPaths::locate(QStandardPaths::GenericDataLocation, shadersDir + QStringLiteral("lookingglass.frag")); m_shader = ShaderManager::instance()->loadFragmentShader(ShaderManager::SimpleShader, fragmentshader); if (m_shader->isValid()) { ShaderBinder binder(m_shader); m_shader->setUniform("u_textureSize", QVector2D(screenSize.width(), screenSize.height())); } else { qCritical() << "The shader failed to load!" << endl; return false; } m_vbo = new GLVertexBuffer(GLVertexBuffer::Static); QVector verts; QVector texcoords; texcoords << screenSize.width() << 0.0; verts << screenSize.width() << 0.0; texcoords << 0.0 << 0.0; verts << 0.0 << 0.0; texcoords << 0.0 << screenSize.height(); verts << 0.0 << screenSize.height(); texcoords << 0.0 << screenSize.height(); verts << 0.0 << screenSize.height(); texcoords << screenSize.width() << screenSize.height(); verts << screenSize.width() << screenSize.height(); texcoords << screenSize.width() << 0.0; verts << screenSize.width() << 0.0; m_vbo->setData(6, 2, verts.constData(), texcoords.constData()); return true; } void LookingGlassEffect::toggle() { if (target_zoom == 1.0f) { target_zoom = 2.0f; if (!polling) { polling = true; effects->startMousePolling(); } m_enabled = true; } else { target_zoom = 1.0f; if (polling) { polling = false; effects->stopMousePolling(); } if (zoom == target_zoom) { m_enabled = false; } } effects->addRepaint(cursorPos().x() - radius, cursorPos().y() - radius, 2 * radius, 2 * radius); } void LookingGlassEffect::zoomIn() { target_zoom = qMin(7.0, target_zoom + 0.5); m_enabled = true; if (!polling) { polling = true; effects->startMousePolling(); } effects->addRepaint(cursorPos().x() - radius, cursorPos().y() - radius, 2 * radius, 2 * radius); } void LookingGlassEffect::zoomOut() { target_zoom -= 0.5; if (target_zoom < 1) { target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } if (zoom == target_zoom) { m_enabled = false; } } effects->addRepaint(cursorPos().x() - radius, cursorPos().y() - radius, 2 * radius, 2 * radius); } void LookingGlassEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (zoom != target_zoom) { double diff = time / animationTime(500.0); if (target_zoom > zoom) zoom = qMin(zoom * qMax(1.0 + diff, 1.2), target_zoom); else zoom = qMax(zoom * qMin(1.0 - diff, 0.8), target_zoom); qCDebug(KWINEFFECTS) << "zoom is now " << zoom; radius = qBound((double)initialradius, initialradius * zoom, 3.5 * initialradius); if (zoom <= 1.0f) { m_enabled = false; } effects->addRepaint(cursorPos().x() - radius, cursorPos().y() - radius, 2 * radius, 2 * radius); } if (m_valid && m_enabled) { data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; // Start rendering to texture GLRenderTarget::pushRenderTarget(m_fbo); } effects->prePaintScreen(data, time); } void LookingGlassEffect::slotMouseChanged(const QPoint& pos, const QPoint& old, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers) { if (pos != old && m_enabled) { effects->addRepaint(pos.x() - radius, pos.y() - radius, 2 * radius, 2 * radius); effects->addRepaint(old.x() - radius, old.y() - radius, 2 * radius, 2 * radius); } } void LookingGlassEffect::postPaintScreen() { // Call the next effect. effects->postPaintScreen(); if (m_valid && m_enabled) { // Disable render texture GLRenderTarget* target = GLRenderTarget::popRenderTarget(); assert(target == m_fbo); Q_UNUSED(target); m_texture->bind(); m_texture->generateMipmaps(); // Use the shader ShaderBinder binder(m_shader); m_shader->setUniform("u_zoom", (float)zoom); m_shader->setUniform("u_radius", (float)radius); m_shader->setUniform("u_cursor", QVector2D(cursorPos().x(), cursorPos().y())); m_vbo->render(GL_TRIANGLES); m_texture->unbind(); } } bool LookingGlassEffect::isActive() const { return m_valid && m_enabled; } } // namespace #include "lookingglass.moc" diff --git a/libkwineffects/kwingltexture.cpp b/libkwineffects/kwingltexture.cpp index 8eb8a8298..beed20823 100644 --- a/libkwineffects/kwingltexture.cpp +++ b/libkwineffects/kwingltexture.cpp @@ -1,541 +1,547 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006-2007 Rivo Laks Copyright (C) 2010, 2011 Martin Gräßlin Copyright (C) 2012 Philipp Knechtges 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 "kwinconfig.h" // KWIN_HAVE_OPENGL #include "kwinglplatform.h" #include "kwinglutils_funcs.h" #include "kwinglutils.h" #include "kwingltexture_p.h" #include #include #include #include #include #include namespace KWin { //**************************************** // GLTexture //**************************************** bool GLTexturePrivate::s_supportsFramebufferObjects = false; bool GLTexturePrivate::s_supportsARGB32 = false; bool GLTexturePrivate::s_supportsUnpack = false; uint GLTexturePrivate::s_textureObjectCounter = 0; uint GLTexturePrivate::s_fbo = 0; GLTexture::GLTexture() : d_ptr(new GLTexturePrivate()) { } GLTexture::GLTexture(GLTexturePrivate& dd) : d_ptr(&dd) { } GLTexture::GLTexture(const GLTexture& tex) : d_ptr(tex.d_ptr) { } GLTexture::GLTexture(const QImage& image, GLenum target) : d_ptr(new GLTexturePrivate()) { Q_D(GLTexture); if (image.isNull()) return; d->m_target = target; if (d->m_target != GL_TEXTURE_RECTANGLE_ARB) { d->m_scale.setWidth(1.0 / image.width()); d->m_scale.setHeight(1.0 / image.height()); - d->m_canUseMipmaps = true; } else { d->m_scale.setWidth(1.0); d->m_scale.setHeight(1.0); - d->m_canUseMipmaps = false; } d->m_size = image.size(); d->m_yInverted = true; + d->m_canUseMipmaps = false; + d->m_mipLevels = 1; d->updateMatrix(); glGenTextures(1, &d->m_texture); bind(); if (!GLPlatform::instance()->isGLES()) { const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + glTexParameteri(d->m_target, GL_TEXTURE_MAX_LEVEL, d->m_mipLevels - 1); glTexImage2D(d->m_target, 0, GL_RGBA8, im.width(), im.height(), 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, im.bits()); } else { if (d->s_supportsARGB32) { const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); glTexImage2D(d->m_target, 0, GL_BGRA_EXT, im.width(), im.height(), 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.bits()); } else { const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); glTexImage2D(d->m_target, 0, GL_RGBA, im.width(), im.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, im.bits()); } } unbind(); setFilter(GL_LINEAR); } GLTexture::GLTexture(const QPixmap& pixmap, GLenum target) : GLTexture(pixmap.toImage(), target) { } GLTexture::GLTexture(const QString& fileName) : GLTexture(QImage(fileName)) { } -GLTexture::GLTexture(int width, int height) +GLTexture::GLTexture(int width, int height, int levels) : d_ptr(new GLTexturePrivate()) { Q_D(GLTexture); d->m_target = GL_TEXTURE_2D; d->m_scale.setWidth(1.0 / width); d->m_scale.setHeight(1.0 / height); d->m_size = QSize(width, height); + d->m_canUseMipmaps = levels > 1; + d->m_mipLevels = levels; d->m_canUseMipmaps = true; + d->m_filter = levels > 1 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST; d->updateMatrix(); glGenTextures(1, &d->m_texture); bind(); if (!GLPlatform::instance()->isGLES()) { + glTexParameteri(d->m_target, GL_TEXTURE_MAX_LEVEL, levels - 1); glTexImage2D(d->m_target, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr); } else { // The format parameter in glTexSubImage() must match the internal format // of the texture, so it's important that we allocate the texture with // the format that will be used in update() and clear(). const GLenum format = d->s_supportsARGB32 ? GL_BGRA_EXT : GL_RGBA; glTexImage2D(d->m_target, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, nullptr); } unbind(); } -GLTexture::GLTexture(const QSize &size) - : GLTexture(size.width(), size.height()) +GLTexture::GLTexture(const QSize &size, int levels) + : GLTexture(size.width(), size.height(), levels) { } GLTexture::~GLTexture() { } GLTexture& GLTexture::operator = (const GLTexture& tex) { d_ptr = tex.d_ptr; return *this; } GLTexturePrivate::GLTexturePrivate() { m_texture = 0; m_target = 0; m_filter = GL_NEAREST; m_wrapMode = GL_REPEAT; m_yInverted = false; m_canUseMipmaps = false; + m_mipLevels = 1; m_markedDirty = false; m_unnormalizeActive = 0; m_normalizeActive = 0; m_vbo = nullptr; m_filterChanged = true; m_wrapModeChanged = false; ++s_textureObjectCounter; } GLTexturePrivate::~GLTexturePrivate() { if (m_vbo != nullptr) { delete m_vbo; } if (m_texture != 0) { glDeleteTextures(1, &m_texture); } // Delete the FBO if this is the last Texture if (--s_textureObjectCounter == 0 && s_fbo) { glDeleteFramebuffers(1, &s_fbo); s_fbo = 0; } } void GLTexturePrivate::initStatic() { if (!GLPlatform::instance()->isGLES()) { s_supportsFramebufferObjects = hasGLVersion(3, 0) || hasGLExtension("GL_ARB_framebuffer_object") || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_object")); s_supportsARGB32 = true; s_supportsUnpack = true; } else { s_supportsFramebufferObjects = true; // QImage::Format_ARGB32_Premultiplied is a packed-pixel format, so it's only // equivalent to GL_BGRA/GL_UNSIGNED_BYTE on little-endian systems. s_supportsARGB32 = QSysInfo::ByteOrder == QSysInfo::LittleEndian && hasGLExtension(QByteArrayLiteral("GL_EXT_texture_format_BGRA8888")); s_supportsUnpack = hasGLExtension(QByteArrayLiteral("GL_EXT_unpack_subimage")); } } void GLTexturePrivate::cleanup() { s_supportsFramebufferObjects = false; s_supportsARGB32 = false; } bool GLTexture::isNull() const { Q_D(const GLTexture); return None == d->m_texture; } QSize GLTexture::size() const { Q_D(const GLTexture); return d->m_size; } void GLTexture::update(const QImage &image, const QPoint &offset, const QRect &src) { if (image.isNull() || isNull()) return; Q_D(GLTexture); bool useUnpack = !src.isNull() && d->s_supportsUnpack && d->s_supportsARGB32 && image.format() == QImage::Format_ARGB32_Premultiplied; int width = image.width(); int height = image.height(); QImage tmpImage; if (!src.isNull()) { if (d->s_supportsUnpack) { glPixelStorei(GL_UNPACK_ROW_LENGTH, image.width()); glPixelStorei(GL_UNPACK_SKIP_PIXELS, src.x()); glPixelStorei(GL_UNPACK_SKIP_ROWS, src.y()); } else { tmpImage = image.copy(src); } width = src.width(); height = src.height(); } const QImage &img = tmpImage.isNull() ? image : tmpImage; bind(); if (!GLPlatform::instance()->isGLES()) { const QImage im = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, im.bits()); } else { if (d->s_supportsARGB32) { const QImage im = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.bits()); } else { const QImage im = img.convertToFormat(QImage::Format_RGBA8888_Premultiplied); glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, GL_RGBA, GL_UNSIGNED_BYTE, im.bits()); } } unbind(); if (useUnpack) { glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); } } void GLTexture::discard() { d_ptr = new GLTexturePrivate(); } void GLTexture::bind() { Q_D(GLTexture); glBindTexture(d->m_target, d->m_texture); if (d->m_markedDirty) { d->onDamage(); } if (d->m_filterChanged) { GLenum minFilter = GL_NEAREST; GLenum magFilter = GL_NEAREST; switch (d->m_filter) { case GL_NEAREST: minFilter = magFilter = GL_NEAREST; break; case GL_LINEAR: minFilter = magFilter = GL_LINEAR; break; case GL_NEAREST_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: magFilter = GL_NEAREST; minFilter = d->m_canUseMipmaps ? d->m_filter : GL_NEAREST; break; case GL_LINEAR_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_LINEAR: magFilter = GL_LINEAR; minFilter = d->m_canUseMipmaps ? d->m_filter : GL_LINEAR; break; } glTexParameteri(d->m_target, GL_TEXTURE_MIN_FILTER, minFilter); glTexParameteri(d->m_target, GL_TEXTURE_MAG_FILTER, magFilter); d->m_filterChanged = false; } if (d->m_wrapModeChanged) { glTexParameteri(d->m_target, GL_TEXTURE_WRAP_S, d->m_wrapMode); glTexParameteri(d->m_target, GL_TEXTURE_WRAP_T, d->m_wrapMode); d->m_wrapModeChanged = false; } } void GLTexture::generateMipmaps() { Q_D(GLTexture); if (d->m_canUseMipmaps && d->s_supportsFramebufferObjects) glGenerateMipmap(d->m_target); } void GLTexture::unbind() { Q_D(GLTexture); glBindTexture(d->m_target, 0); } void GLTexture::render(QRegion region, const QRect& rect, bool hardwareClipping) { Q_D(GLTexture); if (rect.size() != d->m_cachedSize) { d->m_cachedSize = rect.size(); QRect r(rect); r.moveTo(0, 0); if (!d->m_vbo) { d->m_vbo = new GLVertexBuffer(KWin::GLVertexBuffer::Static); } const float verts[ 4 * 2 ] = { // NOTICE: r.x/y could be replaced by "0", but that would make it unreadable... static_cast(r.x()), static_cast(r.y()), static_cast(r.x()), static_cast(r.y() + rect.height()), static_cast(r.x() + rect.width()), static_cast(r.y()), static_cast(r.x() + rect.width()), static_cast(r.y() + rect.height()) }; const float texWidth = (target() == GL_TEXTURE_RECTANGLE_ARB) ? width() : 1.0f; const float texHeight = (target() == GL_TEXTURE_RECTANGLE_ARB) ? height() : 1.0f; const float texcoords[ 4 * 2 ] = { 0.0f, d->m_yInverted ? 0.0f : texHeight, // y needs to be swapped (normalized coords) 0.0f, d->m_yInverted ? texHeight : 0.0f, texWidth, d->m_yInverted ? 0.0f : texHeight, texWidth, d->m_yInverted ? texHeight : 0.0f }; d->m_vbo->setData(4, 2, verts, texcoords); } QMatrix4x4 translation; translation.translate(rect.x(), rect.y()); GLShader *shader = ShaderManager::instance()->getBoundShader(); shader->setUniform(GLShader::Offset, QVector2D(rect.x(), rect.y())); shader->setUniform(GLShader::WindowTransformation, translation); d->m_vbo->render(region, GL_TRIANGLE_STRIP, hardwareClipping); shader->setUniform(GLShader::WindowTransformation, QMatrix4x4()); } GLuint GLTexture::texture() const { Q_D(const GLTexture); return d->m_texture; } GLenum GLTexture::target() const { Q_D(const GLTexture); return d->m_target; } GLenum GLTexture::filter() const { Q_D(const GLTexture); return d->m_filter; } void GLTexture::clear() { Q_D(GLTexture); if (!GLTexturePrivate::s_fbo && GLRenderTarget::supported() && GLPlatform::instance()->driver() != Driver_Catalyst) // fail. -> bug #323065 glGenFramebuffers(1, &GLTexturePrivate::s_fbo); if (GLTexturePrivate::s_fbo) { // Clear the texture glBindFramebuffer(GL_FRAMEBUFFER, GLTexturePrivate::s_fbo); glClearColor(0, 0, 0, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, d->m_texture, 0); glClear(GL_COLOR_BUFFER_BIT); glBindFramebuffer(GL_FRAMEBUFFER, 0); } else { if (const int size = width()*height()) { uint32_t *buffer = new uint32_t[size]; memset(buffer, 0, size*sizeof(uint32_t)); bind(); if (!GLPlatform::instance()->isGLES()) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width(), height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); } else { const GLenum format = d->s_supportsARGB32 ? GL_BGRA_EXT : GL_RGBA; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width(), height(), format, GL_UNSIGNED_BYTE, buffer); } unbind(); delete[] buffer; } } } bool GLTexture::isDirty() const { Q_D(const GLTexture); return d->m_markedDirty; } void GLTexture::setFilter(GLenum filter) { Q_D(GLTexture); if (filter != d->m_filter) { d->m_filter = filter; d->m_filterChanged = true; } } void GLTexture::setWrapMode(GLenum mode) { Q_D(GLTexture); if (mode != d->m_wrapMode) { d->m_wrapMode = mode; d->m_wrapModeChanged=true; } } void GLTexturePrivate::onDamage() { // No-op } void GLTexture::setDirty() { Q_D(GLTexture); d->m_markedDirty = true; } void GLTexturePrivate::updateMatrix() { m_matrix[NormalizedCoordinates].setToIdentity(); m_matrix[UnnormalizedCoordinates].setToIdentity(); if (m_target == GL_TEXTURE_RECTANGLE_ARB) m_matrix[NormalizedCoordinates].scale(m_size.width(), m_size.height()); else m_matrix[UnnormalizedCoordinates].scale(1.0 / m_size.width(), 1.0 / m_size.height()); if (!m_yInverted) { m_matrix[NormalizedCoordinates].translate(0.0, 1.0); m_matrix[NormalizedCoordinates].scale(1.0, -1.0); m_matrix[UnnormalizedCoordinates].translate(0.0, m_size.height()); m_matrix[UnnormalizedCoordinates].scale(1.0, -1.0); } } bool GLTexture::isYInverted() const { Q_D(const GLTexture); return d->m_yInverted; } void GLTexture::setYInverted(bool inverted) { Q_D(GLTexture); d->m_yInverted = inverted; d->updateMatrix(); } int GLTexture::width() const { Q_D(const GLTexture); return d->m_size.width(); } int GLTexture::height() const { Q_D(const GLTexture); return d->m_size.height(); } QMatrix4x4 GLTexture::matrix(TextureCoordinateType type) const { Q_D(const GLTexture); return d->m_matrix[type]; } bool GLTexture::framebufferObjectSupported() { return GLTexturePrivate::s_supportsFramebufferObjects; } } // namespace KWin diff --git a/libkwineffects/kwingltexture.h b/libkwineffects/kwingltexture.h index d559ab0bb..3b9b01297 100644 --- a/libkwineffects/kwingltexture.h +++ b/libkwineffects/kwingltexture.h @@ -1,120 +1,120 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006-2007 Rivo Laks Copyright (C) 2010, 2011 Martin Gräßlin 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_GLTEXTURE_H #define KWIN_GLTEXTURE_H #include #include #include #include #include #include class QImage; class QPixmap; /** @addtogroup kwineffects */ /** @{ */ namespace KWin { class GLVertexBuffer; class GLTexturePrivate; enum TextureCoordinateType { NormalizedCoordinates = 0, UnnormalizedCoordinates }; class KWINGLUTILS_EXPORT GLTexture { public: GLTexture(); GLTexture(const GLTexture& tex); explicit GLTexture(const QImage& image, GLenum target = GL_TEXTURE_2D); explicit GLTexture(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D); explicit GLTexture(const QString& fileName); - GLTexture(int width, int height); - explicit GLTexture(const QSize &size); + GLTexture(int width, int height, int levels = 1); + explicit GLTexture(const QSize &size, int levels = 1); virtual ~GLTexture(); GLTexture & operator = (const GLTexture& tex); bool isNull() const; QSize size() const; int width() const; int height() const; /** * @since 4.7 **/ bool isYInverted() const; /** * @since 4.8 **/ void setYInverted(bool inverted); /** * Returns a matrix that transforms texture coordinates of the given type, * taking the texture target and the y-inversion flag into account. * * @since 4.11 */ QMatrix4x4 matrix(TextureCoordinateType type) const; void update(const QImage& image, const QPoint &offset = QPoint(0, 0), const QRect &src = QRect()); virtual void discard(); void bind(); void unbind(); void render(QRegion region, const QRect& rect, bool hardwareClipping = false); GLuint texture() const; GLenum target() const; GLenum filter() const; /** @short * Make the texture fully transparent * Warning: this clobbers the current framebuffer binding except on fglrx */ void clear(); bool isDirty() const; void setFilter(GLenum filter); void setWrapMode(GLenum mode); void setDirty(); void generateMipmaps(); static bool framebufferObjectSupported(); protected: QExplicitlySharedDataPointer d_ptr; GLTexture(GLTexturePrivate& dd); private: Q_DECLARE_PRIVATE(GLTexture) }; } // namespace /** @} */ #endif diff --git a/libkwineffects/kwingltexture_p.h b/libkwineffects/kwingltexture_p.h index 3cbb0bb12..d44b20d69 100644 --- a/libkwineffects/kwingltexture_p.h +++ b/libkwineffects/kwingltexture_p.h @@ -1,82 +1,83 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006-2007 Rivo Laks Copyright (C) 2010, 2011 Martin Gräßlin Copyright (C) 2011 Philipp Knechtges 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_GLTEXTURE_P_H #define KWIN_GLTEXTURE_P_H #include "kwinconfig.h" // KWIN_HAVE_OPENGL #include #include #include #include namespace KWin { // forward declarations class GLVertexBuffer; class KWINGLUTILS_EXPORT GLTexturePrivate : public QSharedData { public: GLTexturePrivate(); virtual ~GLTexturePrivate(); virtual void onDamage(); void updateMatrix(); GLuint m_texture; GLenum m_target; GLenum m_filter; GLenum m_wrapMode; QSize m_size; QSizeF m_scale; // to un-normalize GL_TEXTURE_2D QMatrix4x4 m_matrix[2]; bool m_yInverted; // texture is y-inverted bool m_canUseMipmaps; bool m_markedDirty; bool m_filterChanged; bool m_wrapModeChanged; + int m_mipLevels; int m_unnormalizeActive; // 0 - no, otherwise refcount int m_normalizeActive; // 0 - no, otherwise refcount GLVertexBuffer* m_vbo; QSize m_cachedSize; static void initStatic(); static bool s_supportsFramebufferObjects; static bool s_supportsARGB32; static bool s_supportsUnpack; static GLuint s_fbo; static uint s_textureObjectCounter; private: friend void KWin::cleanupGL(); static void cleanup(); Q_DISABLE_COPY(GLTexturePrivate) }; } // namespace #endif