You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1711 lines
63 KiB
1711 lines
63 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org> |
|
Copyright (C) 2009, 2010, 2011 Martin Gräßlin <mgraesslin@kde.org> |
|
|
|
Based on glcompmgr code by Felix Bellaby. |
|
Using code from Compiz and Beryl. |
|
|
|
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 <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
|
|
|
|
/* |
|
This is the OpenGL-based compositing code. It is the primary and most powerful |
|
compositing backend. |
|
|
|
Sources and other compositing managers: |
|
======================================= |
|
|
|
- http://opengl.org |
|
- documentation |
|
- OpenGL Redbook (http://opengl.org/documentation/red_book/ - note it's only version 1.1) |
|
- GLX docs (http://opengl.org/documentation/specs/glx/glx1.4.pdf) |
|
- extensions docs (http://www.opengl.org/registry/) |
|
|
|
- glcompmgr |
|
- http://lists.freedesktop.org/archives/xorg/2006-July/017006.html , |
|
- http://www.mail-archive.com/compiz%40lists.freedesktop.org/msg00023.html |
|
- simple and easy to understand |
|
- works even without texture_from_pixmap extension |
|
- claims to support several different gfx cards |
|
- compile with something like |
|
"gcc -Wall glcompmgr-0.5.c `pkg-config --cflags --libs glib-2.0` -lGL -lXcomposite -lXdamage -L/usr/X11R6/lib" |
|
|
|
- compiz |
|
- git clone git://anongit.freedesktop.org/git/xorg/app/compiz |
|
- the ultimate <whatever> |
|
- glxcompmgr |
|
- git clone git://anongit.freedesktop.org/git/xorg/app/glxcompgr |
|
- a rather old version of compiz, but also simpler and as such simpler |
|
to understand |
|
|
|
- beryl |
|
- a fork of Compiz |
|
- http://beryl-project.org |
|
- git clone git://anongit.beryl-project.org/beryl/beryl-core (or beryl-plugins etc. , |
|
the full list should be at git://anongit.beryl-project.org/beryl/) |
|
|
|
- libcm (metacity) |
|
- cvs -d :pserver:anonymous@anoncvs.gnome.org:/cvs/gnome co libcm |
|
- not much idea about it, the model differs a lot from KWin/Compiz/Beryl |
|
- does not seem to be very powerful or with that much development going on |
|
|
|
*/ |
|
|
|
#include "scene_opengl.h" |
|
|
|
#include <kxerrorhandler.h> |
|
|
|
#include <kwinglplatform.h> |
|
|
|
#include "utils.h" |
|
#include "client.h" |
|
#include "deleted.h" |
|
#include "effects.h" |
|
#include "overlaywindow.h" |
|
|
|
#include <math.h> |
|
|
|
// turns on checks for opengl errors in various places (for easier finding of them) |
|
// normally only few of them are enabled |
|
//#define CHECK_GL_ERROR |
|
|
|
#include <X11/extensions/Xcomposite.h> |
|
|
|
#include <qpainter.h> |
|
#include <QVector2D> |
|
#include <QVector4D> |
|
#include <QMatrix4x4> |
|
|
|
namespace KWin |
|
{ |
|
|
|
extern int currentRefreshRate(); |
|
|
|
//**************************************** |
|
// SceneOpenGL |
|
//**************************************** |
|
|
|
bool SceneOpenGL::db; // destination drawable is double-buffered |
|
|
|
#ifdef KWIN_HAVE_OPENGLES |
|
#include "scene_opengl_egl.cpp" |
|
#else |
|
#include "scene_opengl_glx.cpp" |
|
#endif |
|
|
|
bool SceneOpenGL::initFailed() const |
|
{ |
|
return !init_ok; |
|
} |
|
|
|
bool SceneOpenGL::selectMode() |
|
{ |
|
if (!initDrawableConfigs()) |
|
return false; |
|
return true; |
|
} |
|
|
|
QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const |
|
{ |
|
QMatrix4x4 matrix; |
|
|
|
if (!(mask & PAINT_SCREEN_TRANSFORMED)) |
|
return matrix; |
|
|
|
matrix.translate(data.xTranslate, data.yTranslate, data.zTranslate); |
|
matrix.scale(data.xScale, data.yScale, data.zScale); |
|
|
|
if (!data.rotation) |
|
return matrix; |
|
|
|
// Apply the rotation |
|
const qreal xAxis = (data.rotation->axis == RotationData::XAxis ? 1.0 : 0.0); |
|
const qreal yAxis = (data.rotation->axis == RotationData::YAxis ? 1.0 : 0.0); |
|
const qreal zAxis = (data.rotation->axis == RotationData::ZAxis ? 1.0 : 0.0); |
|
|
|
matrix.translate(data.rotation->xRotationPoint, |
|
data.rotation->yRotationPoint, |
|
data.rotation->zRotationPoint); |
|
|
|
matrix.rotate(data.rotation->angle, xAxis, yAxis, zAxis); |
|
|
|
matrix.translate(-data.rotation->xRotationPoint, |
|
-data.rotation->yRotationPoint, |
|
-data.rotation->zRotationPoint); |
|
|
|
return matrix; |
|
} |
|
|
|
void SceneOpenGL::paintGenericScreen(int mask, ScreenPaintData data) |
|
{ |
|
ShaderManager *shaderManager = ShaderManager::instance(); |
|
const bool useShader = shaderManager->isValid(); |
|
const QMatrix4x4 matrix = transformation(mask, data); |
|
|
|
if (useShader) { |
|
GLShader *shader = shaderManager->pushShader(ShaderManager::GenericShader); |
|
shader->setUniform(GLShader::ScreenTransformation, matrix); |
|
} else { |
|
pushMatrix(matrix); |
|
} |
|
|
|
Scene::paintGenericScreen(mask, data); |
|
|
|
if (useShader) |
|
shaderManager->popShader(); |
|
else |
|
popMatrix(); |
|
} |
|
|
|
void SceneOpenGL::paintBackground(QRegion region) |
|
{ |
|
PaintClipper pc(region); |
|
if (!PaintClipper::clip()) { |
|
glClearColor(0, 0, 0, 1); |
|
glClear(GL_COLOR_BUFFER_BIT); |
|
return; |
|
} |
|
if (pc.clip() && pc.paintArea().isEmpty()) |
|
return; // no background to paint |
|
QVector<float> verts; |
|
for (PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) { |
|
QRect r = iterator.boundingRect(); |
|
verts << r.x() + r.width() << r.y(); |
|
verts << r.x() << r.y(); |
|
verts << r.x() << r.y() + r.height(); |
|
verts << r.x() << r.y() + r.height(); |
|
verts << r.x() + r.width() << r.y() + r.height(); |
|
verts << r.x() + r.width() << r.y(); |
|
} |
|
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); |
|
vbo->reset(); |
|
vbo->setUseColor(true); |
|
vbo->setData(verts.count() / 2, 2, verts.data(), NULL); |
|
const bool useShader = ShaderManager::instance()->isValid(); |
|
if (useShader) { |
|
GLShader *shader = ShaderManager::instance()->pushShader(ShaderManager::ColorShader); |
|
shader->setUniform(GLShader::Offset, QVector2D(0, 0)); |
|
} |
|
vbo->render(GL_TRIANGLES); |
|
if (useShader) { |
|
ShaderManager::instance()->popShader(); |
|
} |
|
} |
|
|
|
void SceneOpenGL::windowAdded(Toplevel* c) |
|
{ |
|
assert(!windows.contains(c)); |
|
windows[ c ] = new Window(c); |
|
connect(c, SIGNAL(opacityChanged(KWin::Toplevel*,qreal)), SLOT(windowOpacityChanged(KWin::Toplevel*))); |
|
connect(c, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), SLOT(windowGeometryShapeChanged(KWin::Toplevel*))); |
|
connect(c, SIGNAL(windowClosed(KWin::Toplevel*,KWin::Deleted*)), SLOT(windowClosed(KWin::Toplevel*,KWin::Deleted*))); |
|
c->effectWindow()->setSceneWindow(windows[ c ]); |
|
c->getShadow(); |
|
windows[ c ]->updateShadow(c->shadow()); |
|
} |
|
|
|
void SceneOpenGL::windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted) |
|
{ |
|
assert(windows.contains(c)); |
|
if (deleted != NULL) { |
|
// replace c with deleted |
|
Window* w = windows.take(c); |
|
w->updateToplevel(deleted); |
|
if (w->shadow()) { |
|
w->shadow()->setToplevel(deleted); |
|
} |
|
windows[ deleted ] = w; |
|
} else { |
|
delete windows.take(c); |
|
c->effectWindow()->setSceneWindow(NULL); |
|
} |
|
} |
|
|
|
void SceneOpenGL::windowDeleted(Deleted* c) |
|
{ |
|
assert(windows.contains(c)); |
|
delete windows.take(c); |
|
c->effectWindow()->setSceneWindow(NULL); |
|
} |
|
|
|
void SceneOpenGL::windowGeometryShapeChanged(KWin::Toplevel* c) |
|
{ |
|
if (!windows.contains(c)) // this is ok, shape is not valid |
|
return; // by default |
|
Window* w = windows[ c ]; |
|
w->discardShape(); |
|
w->checkTextureSize(); |
|
} |
|
|
|
void SceneOpenGL::windowOpacityChanged(KWin::Toplevel* t) |
|
{ |
|
Q_UNUSED(t) |
|
#if 0 // not really needed, windows are painted on every repaint |
|
// and opacity is used when applying texture, not when |
|
// creating it |
|
if (!windows.contains(c)) // this is ok, texture is created |
|
return; // on demand |
|
Window* w = windows[ c ]; |
|
w->discardTexture(); |
|
#endif |
|
} |
|
|
|
//**************************************** |
|
// SceneOpenGL::Texture |
|
//**************************************** |
|
|
|
SceneOpenGL::Texture::Texture() : GLTexture(*new TexturePrivate()) |
|
{ |
|
} |
|
|
|
SceneOpenGL::Texture::Texture(TexturePrivate& dd) : GLTexture(dd) |
|
{ |
|
} |
|
|
|
SceneOpenGL::Texture::Texture(const SceneOpenGL::Texture& tex) : GLTexture(*tex.d_ptr) |
|
{ |
|
} |
|
|
|
SceneOpenGL::Texture::Texture(const Pixmap& pix, const QSize& size, int depth) |
|
: GLTexture(*new TexturePrivate()) |
|
{ |
|
load(pix, size, depth); |
|
} |
|
|
|
SceneOpenGL::Texture::Texture(const QPixmap& pix, GLenum target) |
|
: GLTexture(*new TexturePrivate()) |
|
{ |
|
load(pix, target); |
|
} |
|
|
|
SceneOpenGL::Texture::~Texture() |
|
{ |
|
} |
|
|
|
SceneOpenGL::Texture& SceneOpenGL::Texture::operator = (const SceneOpenGL::Texture& tex) |
|
{ |
|
d_ptr = tex.d_ptr; |
|
return *this; |
|
} |
|
|
|
void SceneOpenGL::Texture::discard() |
|
{ |
|
d_ptr = new TexturePrivate(); |
|
} |
|
|
|
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, |
|
int depth) |
|
{ |
|
if (pix == None) |
|
return false; |
|
return load(pix, size, depth, |
|
QRegion(0, 0, size.width(), size.height())); |
|
} |
|
|
|
bool SceneOpenGL::Texture::load(const QImage& image, GLenum target) |
|
{ |
|
if (image.isNull()) |
|
return false; |
|
return load(QPixmap::fromImage(image), target); |
|
} |
|
|
|
bool SceneOpenGL::Texture::load(const QPixmap& pixmap, GLenum target) |
|
{ |
|
if (pixmap.isNull()) |
|
return false; |
|
|
|
// Checking whether QPixmap comes with its own X11 Pixmap |
|
if (Extensions::nonNativePixmaps()) { |
|
return GLTexture::load(pixmap.toImage(), target); |
|
} |
|
|
|
// use the X11 pixmap provided by Qt |
|
return load(pixmap.handle(), pixmap.size(), pixmap.depth()); |
|
} |
|
|
|
//**************************************** |
|
// SceneOpenGL::Window |
|
//**************************************** |
|
|
|
SceneOpenGL::Window::Window(Toplevel* c) |
|
: Scene::Window(c) |
|
, texture() |
|
, topTexture() |
|
, leftTexture() |
|
, rightTexture() |
|
, bottomTexture() |
|
{ |
|
} |
|
|
|
SceneOpenGL::Window::~Window() |
|
{ |
|
discardTexture(); |
|
} |
|
|
|
// Bind the window pixmap to an OpenGL texture. |
|
bool SceneOpenGL::Window::bindTexture() |
|
{ |
|
if (!texture.isNull()) { |
|
if (!toplevel->damage().isEmpty()) { |
|
// mipmaps need to be updated |
|
texture.setDirty(); |
|
toplevel->resetDamage(QRect(toplevel->clientPos(), toplevel->clientSize())); |
|
} |
|
return true; |
|
} |
|
// Get the pixmap with the window contents |
|
Pixmap pix = toplevel->windowPixmap(); |
|
if (pix == None) |
|
return false; |
|
|
|
bool success = texture.load(pix, toplevel->size(), toplevel->depth(), |
|
toplevel->damage()); |
|
|
|
if (success) |
|
toplevel->resetDamage(QRect(toplevel->clientPos(), toplevel->clientSize())); |
|
else |
|
kDebug(1212) << "Failed to bind window"; |
|
return success; |
|
} |
|
|
|
void SceneOpenGL::Window::discardTexture() |
|
{ |
|
texture.discard(); |
|
topTexture.discard(); |
|
leftTexture.discard(); |
|
rightTexture.discard(); |
|
bottomTexture.discard(); |
|
} |
|
|
|
// This call is used in SceneOpenGL::windowGeometryShapeChanged(), |
|
// which originally called discardTexture(), however this was causing performance |
|
// problems with the launch feedback icon - large number of texture rebinds. |
|
// Since the launch feedback icon does not resize, only changes shape, it |
|
// is not necessary to rebind the texture (with no strict binding), therefore |
|
// discard the texture only if size changes. |
|
void SceneOpenGL::Window::checkTextureSize() |
|
{ |
|
if (texture.size() != size()) |
|
discardTexture(); |
|
} |
|
|
|
// when the window's composite pixmap is discarded, undo binding it to the texture |
|
void SceneOpenGL::Window::pixmapDiscarded() |
|
{ |
|
texture.discard(); |
|
} |
|
|
|
QMatrix4x4 SceneOpenGL::Window::transformation(int mask, const WindowPaintData &data) const |
|
{ |
|
QMatrix4x4 matrix; |
|
matrix.translate(x(), y()); |
|
|
|
if (!(mask & PAINT_WINDOW_TRANSFORMED)) |
|
return matrix; |
|
|
|
matrix.translate(data.xTranslate, data.yTranslate, data.zTranslate); |
|
matrix.scale(data.xScale, data.yScale, data.zScale); |
|
|
|
if (!data.rotation) |
|
return matrix; |
|
|
|
// Apply the rotation |
|
const qreal xAxis = (data.rotation->axis == RotationData::XAxis ? 1.0 : 0.0); |
|
const qreal yAxis = (data.rotation->axis == RotationData::YAxis ? 1.0 : 0.0); |
|
const qreal zAxis = (data.rotation->axis == RotationData::ZAxis ? 1.0 : 0.0); |
|
|
|
matrix.translate(data.rotation->xRotationPoint, |
|
data.rotation->yRotationPoint, |
|
data.rotation->zRotationPoint); |
|
|
|
matrix.rotate(data.rotation->angle, xAxis, yAxis, zAxis); |
|
|
|
matrix.translate(-data.rotation->xRotationPoint, |
|
-data.rotation->yRotationPoint, |
|
-data.rotation->zRotationPoint); |
|
|
|
return matrix; |
|
} |
|
|
|
// paint the window |
|
void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData data) |
|
{ |
|
// check if there is something to paint (e.g. don't paint if the window |
|
// is only opaque and only PAINT_WINDOW_TRANSLUCENT is requested) |
|
/* HACK: It seems this causes painting glitches, disable temporarily |
|
bool opaque = isOpaque() && data.opacity == 1.0; |
|
if (( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT )) |
|
{ // We are only painting either opaque OR translucent windows, not both |
|
if ( mask & PAINT_WINDOW_OPAQUE && !opaque ) |
|
return; // Only painting opaque and window is translucent |
|
if ( mask & PAINT_WINDOW_TRANSLUCENT && opaque ) |
|
return; // Only painting translucent and window is opaque |
|
}*/ |
|
|
|
if (region.isEmpty()) |
|
return; |
|
|
|
if (region != infiniteRegion() && !(mask & PAINT_WINDOW_TRANSFORMED)) { |
|
WindowQuadList quads; |
|
const QRegion filterRegion = region.translated(-x(), -y()); |
|
// split all quads in bounding rect with the actual rects in the region |
|
foreach (const WindowQuad &quad, data.quads) { |
|
foreach (const QRect &r, filterRegion.rects()) { |
|
const QRectF rf(r); |
|
const QRectF quadRect(QPointF(quad.left(), quad.top()), QPointF(quad.right(), quad.bottom())); |
|
// case 1: completely contains, include and do not check other rects |
|
if (rf.contains(quadRect)) { |
|
quads << quad; |
|
break; |
|
} |
|
// case 2: intersection |
|
if (rf.intersects(quadRect)) { |
|
const QRectF intersected = rf.intersected(quadRect); |
|
quads << quad.makeSubQuad(intersected.left(), intersected.top(), intersected.right(), intersected.bottom()); |
|
} |
|
} |
|
} |
|
data.quads = quads; |
|
} |
|
|
|
if (!bindTexture()) |
|
return; |
|
|
|
// Update the texture filter |
|
if (options->glSmoothScale() != 0 && |
|
(mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED))) |
|
filter = ImageFilterGood; |
|
else |
|
filter = ImageFilterFast; |
|
|
|
texture.setFilter(filter == ImageFilterGood ? GL_LINEAR : GL_NEAREST); |
|
|
|
bool sceneShader = false; |
|
|
|
if (!data.shader && ShaderManager::instance()->isValid()) { |
|
// set the shader for uniform initialising in paint decoration |
|
if ((mask & PAINT_WINDOW_TRANSFORMED) || (mask & PAINT_SCREEN_TRANSFORMED)) { |
|
data.shader = ShaderManager::instance()->pushShader(ShaderManager::GenericShader); |
|
} else { |
|
data.shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); |
|
data.shader->setUniform(GLShader::Offset, QVector2D(x(), y())); |
|
} |
|
sceneShader = true; |
|
} |
|
|
|
const QMatrix4x4 windowTransformation = transformation(mask, data); |
|
|
|
if (data.shader) |
|
data.shader->setUniform(GLShader::WindowTransformation, windowTransformation); |
|
|
|
if (!sceneShader) |
|
pushMatrix(windowTransformation); |
|
|
|
WindowQuadList decoration = data.quads.select(WindowQuadDecoration); |
|
|
|
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); |
|
vbo->reset(); |
|
|
|
// shadow |
|
if (m_shadow) { |
|
paintShadow(region, data); |
|
} |
|
// decorations |
|
Client *client = dynamic_cast<Client*>(toplevel); |
|
Deleted *deleted = dynamic_cast<Deleted*>(toplevel); |
|
if (client || deleted) { |
|
bool noBorder = true; |
|
bool updateDeco = false; |
|
const QPixmap *left = NULL; |
|
const QPixmap *top = NULL; |
|
const QPixmap *right = NULL; |
|
const QPixmap *bottom = NULL; |
|
QRect topRect, leftRect, rightRect, bottomRect; |
|
if (client && !client->noBorder()) { |
|
noBorder = false; |
|
updateDeco = client->decorationPixmapRequiresRepaint(); |
|
client->ensureDecorationPixmapsPainted(); |
|
|
|
client->layoutDecorationRects(leftRect, topRect, rightRect, bottomRect, Client::WindowRelative); |
|
|
|
left = client->leftDecoPixmap(); |
|
top = client->topDecoPixmap(); |
|
right = client->rightDecoPixmap(); |
|
bottom = client->bottomDecoPixmap(); |
|
} |
|
if (deleted && !deleted->noBorder()) { |
|
noBorder = false; |
|
left = deleted->leftDecoPixmap(); |
|
top = deleted->topDecoPixmap(); |
|
right = deleted->rightDecoPixmap(); |
|
bottom = deleted->bottomDecoPixmap(); |
|
deleted->layoutDecorationRects(leftRect, topRect, rightRect, bottomRect); |
|
} |
|
if (!noBorder) { |
|
WindowQuadList topList, leftList, rightList, bottomList; |
|
|
|
foreach (const WindowQuad & quad, decoration) { |
|
if (topRect.contains(QPoint(quad.originalLeft(), quad.originalTop()))) { |
|
topList.append(quad); |
|
continue; |
|
} |
|
if (bottomRect.contains(QPoint(quad.originalLeft(), quad.originalTop()))) { |
|
bottomList.append(quad); |
|
continue; |
|
} |
|
if (leftRect.contains(QPoint(quad.originalLeft(), quad.originalTop()))) { |
|
leftList.append(quad); |
|
continue; |
|
} |
|
if (rightRect.contains(QPoint(quad.originalLeft(), quad.originalTop()))) { |
|
rightList.append(quad); |
|
continue; |
|
} |
|
} |
|
|
|
paintDecoration(top, DecorationTop, region, topRect, data, topList, updateDeco); |
|
paintDecoration(left, DecorationLeft, region, leftRect, data, leftList, updateDeco); |
|
paintDecoration(right, DecorationRight, region, rightRect, data, rightList, updateDeco); |
|
paintDecoration(bottom, DecorationBottom, region, bottomRect, data, bottomList, updateDeco); |
|
} |
|
} |
|
|
|
// paint the content |
|
WindowQuadList contentQuads = data.quads.select(WindowQuadContents); |
|
if (!contentQuads.empty()) { |
|
texture.bind(); |
|
prepareStates(Content, data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader); |
|
renderQuads(mask, region, contentQuads, &texture); |
|
restoreStates(Content, data.opacity * data.contents_opacity, data.brightness, data.saturation, data.shader); |
|
texture.unbind(); |
|
#ifndef KWIN_HAVE_OPENGLES |
|
if (static_cast<SceneOpenGL*>(scene)->debug) { |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
|
renderQuads(mask, region, contentQuads, &texture); |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
|
} |
|
#endif |
|
} |
|
|
|
if (sceneShader) { |
|
ShaderManager::instance()->popShader(); |
|
data.shader = NULL; |
|
} else { |
|
popMatrix(); |
|
} |
|
} |
|
|
|
void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType decorationType, const QRegion& region, const QRect& rect, const WindowPaintData& data, const WindowQuadList& quads, bool updateDeco) |
|
{ |
|
SceneOpenGL::Texture* decorationTexture; |
|
switch(decorationType) { |
|
case DecorationTop: |
|
decorationTexture = &topTexture; |
|
break; |
|
case DecorationLeft: |
|
decorationTexture = &leftTexture; |
|
break; |
|
case DecorationRight: |
|
decorationTexture = &rightTexture; |
|
break; |
|
case DecorationBottom: |
|
decorationTexture = &bottomTexture; |
|
break; |
|
default: |
|
return; |
|
} |
|
if (decoration->isNull()) { |
|
return; |
|
} |
|
if (decorationTexture->isNull() || updateDeco) { |
|
bool success = decorationTexture->load(*decoration); |
|
if (!success) { |
|
kDebug(1212) << "Failed to bind decoartion"; |
|
return; |
|
} |
|
} |
|
|
|
// We have to update the texture although we do not paint anything. |
|
// This is especially needed if we draw the opaque part of the window |
|
// and the decoration in two different passes (as we in Scene::paintSimpleWindow do). |
|
// Otherwise we run into the situation that in the first pass there are some |
|
// pending decoration repaints but we don't paint the decoration and in the |
|
// second pass it's the other way around. |
|
if (quads.isEmpty()) |
|
return; |
|
|
|
if (filter == ImageFilterGood) |
|
decorationTexture->setFilter(GL_LINEAR); |
|
else |
|
decorationTexture->setFilter(GL_NEAREST); |
|
decorationTexture->setWrapMode(GL_CLAMP_TO_EDGE); |
|
decorationTexture->bind(); |
|
|
|
prepareStates(decorationType, data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader); |
|
makeDecorationArrays(quads, rect, decorationTexture); |
|
GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES); |
|
restoreStates(decorationType, data.opacity * data.decoration_opacity, data.brightness, data.saturation, data.shader); |
|
decorationTexture->unbind(); |
|
#ifndef KWIN_HAVE_OPENGLES |
|
if (static_cast<SceneOpenGL*>(scene)->debug) { |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
|
GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES); |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
|
} |
|
#endif |
|
} |
|
|
|
void SceneOpenGL::Window::paintShadow(const QRegion ®ion, const WindowPaintData &data) |
|
{ |
|
WindowQuadList quads = data.quads.select(WindowQuadShadowTopLeft); |
|
quads.append(data.quads.select(WindowQuadShadowTop)); |
|
quads.append(data.quads.select(WindowQuadShadowTopRight)); |
|
quads.append(data.quads.select(WindowQuadShadowRight)); |
|
quads.append(data.quads.select(WindowQuadShadowBottomRight)); |
|
quads.append(data.quads.select(WindowQuadShadowBottom)); |
|
quads.append(data.quads.select(WindowQuadShadowBottomLeft)); |
|
quads.append(data.quads.select(WindowQuadShadowLeft)); |
|
if (quads.isEmpty()) { |
|
return; |
|
} |
|
GLTexture *texture = static_cast<SceneOpenGLShadow*>(m_shadow)->shadowTexture(); |
|
if (!texture) { |
|
return; |
|
} |
|
if (filter == ImageFilterGood) |
|
texture->setFilter(GL_LINEAR); |
|
else |
|
texture->setFilter(GL_NEAREST); |
|
texture->setWrapMode(GL_CLAMP_TO_EDGE); |
|
texture->bind(); |
|
prepareStates(Shadow, data.opacity, data.brightness, data.saturation, data.shader, texture); |
|
renderQuads(0, region, quads, texture, true); |
|
restoreStates(Shadow, data.opacity, data.brightness, data.saturation, data.shader, texture); |
|
texture->unbind(); |
|
#ifndef KWIN_HAVE_OPENGLES |
|
if (static_cast<SceneOpenGL*>(scene)->debug) { |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
|
renderQuads(0, region, quads, texture); |
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
|
} |
|
#endif |
|
} |
|
|
|
void SceneOpenGL::Window::makeDecorationArrays(const WindowQuadList& quads, const QRect &rect, Texture *tex) const |
|
{ |
|
QVector<float> vertices; |
|
QVector<float> texcoords; |
|
vertices.reserve(quads.count() * 6 * 2); |
|
texcoords.reserve(quads.count() * 6 * 2); |
|
float width = rect.width(); |
|
float height = rect.height(); |
|
#ifndef KWIN_HAVE_OPENGLES |
|
if (tex->target() == GL_TEXTURE_RECTANGLE_ARB) { |
|
width = 1.0; |
|
height = 1.0; |
|
} |
|
#endif |
|
foreach (const WindowQuad & quad, quads) { |
|
vertices << quad[ 1 ].x(); |
|
vertices << quad[ 1 ].y(); |
|
vertices << quad[ 0 ].x(); |
|
vertices << quad[ 0 ].y(); |
|
vertices << quad[ 3 ].x(); |
|
vertices << quad[ 3 ].y(); |
|
vertices << quad[ 3 ].x(); |
|
vertices << quad[ 3 ].y(); |
|
vertices << quad[ 2 ].x(); |
|
vertices << quad[ 2 ].y(); |
|
vertices << quad[ 1 ].x(); |
|
vertices << quad[ 1 ].y(); |
|
|
|
if (tex->isYInverted()) { |
|
texcoords << (float)(quad.originalRight() - rect.x()) / width; |
|
texcoords << (float)(quad.originalTop() - rect.y()) / height; |
|
texcoords << (float)(quad.originalLeft() - rect.x()) / width; |
|
texcoords << (float)(quad.originalTop() - rect.y()) / height; |
|
texcoords << (float)(quad.originalLeft() - rect.x()) / width; |
|
texcoords << (float)(quad.originalBottom() - rect.y()) / height; |
|
texcoords << (float)(quad.originalLeft() - rect.x()) / width; |
|
texcoords << (float)(quad.originalBottom() - rect.y()) / height; |
|
texcoords << (float)(quad.originalRight() - rect.x()) / width; |
|
texcoords << (float)(quad.originalBottom() - rect.y()) / height; |
|
texcoords << (float)(quad.originalRight() - rect.x()) / width; |
|
texcoords << (float)(quad.originalTop() - rect.y()) / height; |
|
} else { |
|
texcoords << (float)(quad.originalRight() - rect.x()) / width; |
|
texcoords << 1.0f - (float)(quad.originalTop() - rect.y()) / height; |
|
texcoords << (float)(quad.originalLeft() - rect.x()) / width; |
|
texcoords << 1.0f - (float)(quad.originalTop() - rect.y()) / height; |
|
texcoords << (float)(quad.originalLeft() - rect.x()) / width; |
|
texcoords << 1.0f - (float)(quad.originalBottom() - rect.y()) / height; |
|
texcoords << (float)(quad.originalLeft() - rect.x()) / width; |
|
texcoords << 1.0f - (float)(quad.originalBottom() - rect.y()) / height; |
|
texcoords << (float)(quad.originalRight() - rect.x()) / width; |
|
texcoords << 1.0f - (float)(quad.originalBottom() - rect.y()) / height; |
|
texcoords << (float)(quad.originalRight() - rect.x()) / width; |
|
texcoords << 1.0f - (float)(quad.originalTop() - rect.y()) / height; |
|
} |
|
} |
|
GLVertexBuffer::streamingBuffer()->setData(quads.count() * 6, 2, vertices.data(), texcoords.data()); |
|
} |
|
|
|
void SceneOpenGL::Window::renderQuads(int, const QRegion& region, const WindowQuadList& quads, GLTexture *tex, bool normalized) |
|
{ |
|
if (quads.isEmpty()) |
|
return; |
|
// Render geometry |
|
float* vertices; |
|
float* texcoords; |
|
QSizeF size(tex->size()); |
|
if (normalized) { |
|
size.setWidth(1.0); |
|
size.setHeight(1.0); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
if (tex->target() == GL_TEXTURE_RECTANGLE_ARB) { |
|
size.setWidth(1.0); |
|
size.setHeight(1.0); |
|
} |
|
#endif |
|
quads.makeArrays(&vertices, &texcoords, size, tex->isYInverted()); |
|
GLVertexBuffer::streamingBuffer()->setData(quads.count() * 6, 2, vertices, texcoords); |
|
GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES); |
|
delete[] vertices; |
|
delete[] texcoords; |
|
} |
|
|
|
void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) |
|
{ |
|
if (shader) |
|
prepareShaderRenderStates(type, opacity, brightness, saturation, shader); |
|
else { |
|
Texture *tex = NULL; |
|
switch(type) { |
|
case Content: |
|
tex = &texture; |
|
break; |
|
case DecorationTop: |
|
tex = &topTexture; |
|
break; |
|
case DecorationLeft: |
|
tex = &leftTexture; |
|
break; |
|
case DecorationRight: |
|
tex = &rightTexture; |
|
break; |
|
case DecorationBottom: |
|
tex = &bottomTexture; |
|
break; |
|
default: |
|
return; |
|
} |
|
prepareStates(type, opacity, brightness, saturation, shader, tex); |
|
} |
|
} |
|
|
|
void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture) |
|
{ |
|
if (shader) { |
|
prepareShaderRenderStates(type, opacity, brightness, saturation, shader); |
|
} else { |
|
prepareRenderStates(type, opacity, brightness, saturation, texture); |
|
} |
|
} |
|
|
|
void SceneOpenGL::Window::prepareShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) |
|
{ |
|
// setup blending of transparent windows |
|
#ifndef KWIN_HAVE_OPENGLES |
|
glPushAttrib(GL_ENABLE_BIT); |
|
#endif |
|
bool opaque = isOpaque() && opacity == 1.0; |
|
bool alpha = toplevel->hasAlpha() || type != Content; |
|
if (type != Content) |
|
opaque = false; |
|
if (!opaque) { |
|
glEnable(GL_BLEND); |
|
if (alpha) { |
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
|
} else { |
|
glBlendColor((float)opacity, (float)opacity, (float)opacity, (float)opacity); |
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA); |
|
} |
|
} |
|
|
|
const float rgb = brightness * opacity; |
|
const float a = opacity; |
|
|
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(rgb, rgb, rgb, a)); |
|
shader->setUniform(GLShader::Saturation, saturation); |
|
shader->setUniform(GLShader::AlphaToOne, opaque ? 1 : 0); |
|
} |
|
|
|
void SceneOpenGL::Window::prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex) |
|
{ |
|
#ifdef KWIN_HAVE_OPENGLES |
|
Q_UNUSED(type) |
|
Q_UNUSED(opacity) |
|
Q_UNUSED(brightness) |
|
Q_UNUSED(saturation) |
|
Q_UNUSED(tex) |
|
#else |
|
bool alpha = false; |
|
bool opaque = true; |
|
if (type == Content) { |
|
alpha = toplevel->hasAlpha(); |
|
opaque = isOpaque() && opacity == 1.0; |
|
} else { |
|
alpha = true; |
|
opaque = false; |
|
} |
|
// setup blending of transparent windows |
|
glPushAttrib(GL_ENABLE_BIT); |
|
if (!opaque) { |
|
glEnable(GL_BLEND); |
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
|
} |
|
if (saturation != 1.0 && tex->saturationSupported()) { |
|
// First we need to get the color from [0; 1] range to [0.5; 1] range |
|
glActiveTexture(GL_TEXTURE0); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); |
|
const float scale_constant[] = { 1.0, 1.0, 1.0, 0.5}; |
|
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, scale_constant); |
|
tex->bind(); |
|
|
|
// Then we take dot product of the result of previous pass and |
|
// saturation_constant. This gives us completely unsaturated |
|
// (greyscale) image |
|
// Note that both operands have to be in range [0.5; 1] since opengl |
|
// automatically substracts 0.5 from them |
|
glActiveTexture(GL_TEXTURE1); |
|
float saturation_constant[] = { 0.5 + 0.5 * 0.30, 0.5 + 0.5 * 0.59, 0.5 + 0.5 * 0.11, saturation }; |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); |
|
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant); |
|
tex->bind(); |
|
|
|
// Finally we need to interpolate between the original image and the |
|
// greyscale image to get wanted level of saturation |
|
glActiveTexture(GL_TEXTURE2); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); |
|
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, saturation_constant); |
|
// Also replace alpha by primary color's alpha here |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); |
|
// And make primary color contain the wanted opacity |
|
glColor4f(opacity, opacity, opacity, opacity); |
|
tex->bind(); |
|
|
|
if (alpha || brightness != 1.0f) { |
|
glActiveTexture(GL_TEXTURE3); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); |
|
// The color has to be multiplied by both opacity and brightness |
|
float opacityByBrightness = opacity * brightness; |
|
glColor4f(opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity); |
|
if (alpha) { |
|
// Multiply original texture's alpha by our opacity |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); |
|
} else { |
|
// Alpha will be taken from previous stage |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); |
|
} |
|
tex->bind(); |
|
} |
|
|
|
glActiveTexture(GL_TEXTURE0); |
|
} else if (opacity != 1.0 || brightness != 1.0) { |
|
// the window is additionally configured to have its opacity adjusted, |
|
// do it |
|
float opacityByBrightness = opacity * brightness; |
|
if (alpha) { |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); |
|
glColor4f(opacityByBrightness, opacityByBrightness, opacityByBrightness, |
|
opacity); |
|
} else { |
|
// Multiply color by brightness and replace alpha by opacity |
|
float constant[] = { opacityByBrightness, opacityByBrightness, opacityByBrightness, opacity }; |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT); |
|
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); |
|
} |
|
} else if (!alpha && opaque) { |
|
float constant[] = { 1.0, 1.0, 1.0, 1.0 }; |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); |
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT); |
|
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); |
|
} |
|
#endif |
|
} |
|
|
|
void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) |
|
{ |
|
if (shader) |
|
restoreShaderRenderStates(type, opacity, brightness, saturation, shader); |
|
else { |
|
Texture *tex = NULL; |
|
switch(type) { |
|
case Content: |
|
tex = &texture; |
|
break; |
|
case DecorationTop: |
|
tex = &topTexture; |
|
break; |
|
case DecorationLeft: |
|
tex = &leftTexture; |
|
break; |
|
case DecorationRight: |
|
tex = &rightTexture; |
|
break; |
|
case DecorationBottom: |
|
tex = &bottomTexture; |
|
break; |
|
default: |
|
return; |
|
} |
|
restoreStates(type, opacity, brightness, saturation, shader, tex); |
|
} |
|
} |
|
|
|
void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture) |
|
{ |
|
if (shader) { |
|
restoreShaderRenderStates(type, opacity, brightness, saturation, shader); |
|
} else { |
|
restoreRenderStates(type, opacity, brightness, saturation, texture); |
|
} |
|
} |
|
|
|
void SceneOpenGL::Window::restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader) |
|
{ |
|
Q_UNUSED(brightness); |
|
Q_UNUSED(saturation); |
|
Q_UNUSED(shader); |
|
bool opaque = isOpaque() && opacity == 1.0; |
|
if (type != Content) |
|
opaque = false; |
|
if (!opaque) { |
|
glDisable(GL_BLEND); |
|
} |
|
ShaderManager::instance()->getBoundShader()->setUniform(GLShader::AlphaToOne, 0); |
|
#ifndef KWIN_HAVE_OPENGLES |
|
glPopAttrib(); // ENABLE_BIT |
|
#endif |
|
} |
|
|
|
void SceneOpenGL::Window::restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex) |
|
{ |
|
Q_UNUSED(type) |
|
#ifdef KWIN_HAVE_OPENGLES |
|
Q_UNUSED(opacity) |
|
Q_UNUSED(brightness) |
|
Q_UNUSED(saturation) |
|
Q_UNUSED(tex) |
|
#else |
|
if (opacity != 1.0 || saturation != 1.0 || brightness != 1.0f) { |
|
if (saturation != 1.0 && tex->saturationSupported()) { |
|
glActiveTexture(GL_TEXTURE3); |
|
glDisable(tex->target()); |
|
glActiveTexture(GL_TEXTURE2); |
|
glDisable(tex->target()); |
|
glActiveTexture(GL_TEXTURE1); |
|
glDisable(tex->target()); |
|
glActiveTexture(GL_TEXTURE0); |
|
} |
|
} |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); |
|
glColor4f(0, 0, 0, 0); |
|
|
|
glPopAttrib(); // ENABLE_BIT |
|
#endif |
|
} |
|
|
|
//**************************************** |
|
// SceneOpenGL::EffectFrame |
|
//**************************************** |
|
|
|
SceneOpenGL::Texture* SceneOpenGL::EffectFrame::m_unstyledTexture = NULL; |
|
QPixmap* SceneOpenGL::EffectFrame::m_unstyledPixmap = NULL; |
|
|
|
SceneOpenGL::EffectFrame::EffectFrame(EffectFrameImpl* frame) |
|
: Scene::EffectFrame(frame) |
|
, m_texture(NULL) |
|
, m_textTexture(NULL) |
|
, m_oldTextTexture(NULL) |
|
, m_textPixmap(NULL) |
|
, m_iconTexture(NULL) |
|
, m_oldIconTexture(NULL) |
|
, m_selectionTexture(NULL) |
|
, m_unstyledVBO(NULL) |
|
{ |
|
if (m_effectFrame->style() == EffectFrameUnstyled && !m_unstyledTexture) { |
|
updateUnstyledTexture(); |
|
} |
|
} |
|
|
|
SceneOpenGL::EffectFrame::~EffectFrame() |
|
{ |
|
delete m_texture; |
|
delete m_textTexture; |
|
delete m_textPixmap; |
|
delete m_oldTextTexture; |
|
delete m_iconTexture; |
|
delete m_oldIconTexture; |
|
delete m_selectionTexture; |
|
delete m_unstyledVBO; |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::free() |
|
{ |
|
glFlush(); |
|
delete m_texture; |
|
m_texture = NULL; |
|
delete m_textTexture; |
|
m_textTexture = NULL; |
|
delete m_textPixmap; |
|
m_textPixmap = NULL; |
|
delete m_iconTexture; |
|
m_iconTexture = NULL; |
|
delete m_selectionTexture; |
|
m_selectionTexture = NULL; |
|
delete m_unstyledVBO; |
|
m_unstyledVBO = NULL; |
|
delete m_oldIconTexture; |
|
m_oldIconTexture = NULL; |
|
delete m_oldTextTexture; |
|
m_oldTextTexture = NULL; |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::freeIconFrame() |
|
{ |
|
delete m_iconTexture; |
|
m_iconTexture = NULL; |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::freeTextFrame() |
|
{ |
|
delete m_textTexture; |
|
m_textTexture = NULL; |
|
delete m_textPixmap; |
|
m_textPixmap = NULL; |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::freeSelection() |
|
{ |
|
delete m_selectionTexture; |
|
m_selectionTexture = NULL; |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::crossFadeIcon() |
|
{ |
|
delete m_oldIconTexture; |
|
m_oldIconTexture = m_iconTexture; |
|
m_iconTexture = NULL; |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::crossFadeText() |
|
{ |
|
delete m_oldTextTexture; |
|
m_oldTextTexture = m_textTexture; |
|
m_textTexture = NULL; |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::render(QRegion region, double opacity, double frameOpacity) |
|
{ |
|
if (m_effectFrame->geometry().isEmpty()) |
|
return; // Nothing to display |
|
|
|
region = infiniteRegion(); // TODO: Old region doesn't seem to work with OpenGL |
|
|
|
GLShader* shader = m_effectFrame->shader(); |
|
bool sceneShader = false; |
|
if (!shader && ShaderManager::instance()->isValid()) { |
|
shader = ShaderManager::instance()->pushShader(ShaderManager::SimpleShader); |
|
sceneShader = true; |
|
} else if (shader) { |
|
ShaderManager::instance()->pushShader(shader); |
|
} |
|
|
|
if (shader) { |
|
if (sceneShader) |
|
shader->setUniform(GLShader::Offset, QVector2D(0, 0)); |
|
|
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(1.0, 1.0, 1.0, 1.0)); |
|
shader->setUniform(GLShader::Saturation, 1.0f); |
|
shader->setUniform(GLShader::AlphaToOne, 0); |
|
} |
|
|
|
#ifndef KWIN_HAVE_OPENGLES |
|
glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT); |
|
#endif |
|
glEnable(GL_BLEND); |
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
|
#ifndef KWIN_HAVE_OPENGLES |
|
if (!shader) |
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); |
|
|
|
// TODO: drop the push matrix |
|
glPushMatrix(); |
|
#endif |
|
|
|
// Render the actual frame |
|
if (m_effectFrame->style() == EffectFrameUnstyled) { |
|
if (!m_unstyledVBO) { |
|
m_unstyledVBO = new GLVertexBuffer(GLVertexBuffer::Static); |
|
QRect area = m_effectFrame->geometry(); |
|
area.moveTo(0, 0); |
|
area.adjust(-5, -5, 5, 5); |
|
|
|
const int roundness = 5; |
|
QVector<float> verts, texCoords; |
|
verts.reserve(84); |
|
texCoords.reserve(84); |
|
|
|
// top left |
|
verts << area.left() << area.top(); |
|
texCoords << 0.0f << 0.0f; |
|
verts << area.left() << area.top() + roundness; |
|
texCoords << 0.0f << 0.5f; |
|
verts << area.left() + roundness << area.top(); |
|
texCoords << 0.5f << 0.0f; |
|
verts << area.left() + roundness << area.top() + roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.left() << area.top() + roundness; |
|
texCoords << 0.0f << 0.5f; |
|
verts << area.left() + roundness << area.top(); |
|
texCoords << 0.5f << 0.0f; |
|
// top |
|
verts << area.left() + roundness << area.top(); |
|
texCoords << 0.5f << 0.0f; |
|
verts << area.left() + roundness << area.top() + roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.right() - roundness << area.top(); |
|
texCoords << 0.5f << 0.0f; |
|
verts << area.left() + roundness << area.top() + roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.right() - roundness << area.top() + roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.right() - roundness << area.top(); |
|
texCoords << 0.5f << 0.0f; |
|
// top right |
|
verts << area.right() - roundness << area.top(); |
|
texCoords << 0.5f << 0.0f; |
|
verts << area.right() - roundness << area.top() + roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.right() << area.top(); |
|
texCoords << 1.0f << 0.0f; |
|
verts << area.right() - roundness << area.top() + roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.right() << area.top() + roundness; |
|
texCoords << 1.0f << 0.5f; |
|
verts << area.right() << area.top(); |
|
texCoords << 1.0f << 0.0f; |
|
// bottom left |
|
verts << area.left() << area.bottom() - roundness; |
|
texCoords << 0.0f << 0.5f; |
|
verts << area.left() << area.bottom(); |
|
texCoords << 0.0f << 1.0f; |
|
verts << area.left() + roundness << area.bottom() - roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.left() + roundness << area.bottom(); |
|
texCoords << 0.5f << 1.0f; |
|
verts << area.left() << area.bottom(); |
|
texCoords << 0.0f << 1.0f; |
|
verts << area.left() + roundness << area.bottom() - roundness; |
|
texCoords << 0.5f << 0.5f; |
|
// bottom |
|
verts << area.left() + roundness << area.bottom() - roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.left() + roundness << area.bottom(); |
|
texCoords << 0.5f << 1.0f; |
|
verts << area.right() - roundness << area.bottom() - roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.left() + roundness << area.bottom(); |
|
texCoords << 0.5f << 1.0f; |
|
verts << area.right() - roundness << area.bottom(); |
|
texCoords << 0.5f << 1.0f; |
|
verts << area.right() - roundness << area.bottom() - roundness; |
|
texCoords << 0.5f << 0.5f; |
|
// bottom right |
|
verts << area.right() - roundness << area.bottom() - roundness; |
|
texCoords << 0.5f << 0.5f; |
|
verts << area.right() - roundness << area.bottom(); |
|
texCoords << 0.5f << 1.0f; |
|
verts << area.right() << area.bottom() - roundness; |
|
texCoords << 1.0f << 0.5f; |
|
verts << area.right() - roundness << area.bottom(); |
|
texCoords << 0.5f << 1.0f; |
|
verts << area.right() << area.bottom(); |
|
texCoords << 1.0f << 1.0f; |
|
verts << area.right() << area.bottom() - roundness; |
|
texCoords << 1.0f << 0.5f; |
|
// center |
|
verts << area.left() << area.top() + roundness; |
|
texCoords << 0.0f << 0.5f; |
|
verts << area.left() << area.bottom() - roundness; |
|
texCoords << 0.0f << 0.5f; |
|
verts << area.right() << area.top() + roundness; |
|
texCoords << 1.0f << 0.5f; |
|
verts << area.left() << area.bottom() - roundness; |
|
texCoords << 0.0f << 0.5f; |
|
verts << area.right() << area.bottom() - roundness; |
|
texCoords << 1.0f << 0.5f; |
|
verts << area.right() << area.top() + roundness; |
|
texCoords << 1.0f << 0.5f; |
|
|
|
m_unstyledVBO->setData(verts.count() / 2, 2, verts.data(), texCoords.data()); |
|
} |
|
|
|
if (shader) { |
|
const float a = opacity * frameOpacity; |
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
else |
|
glColor4f(0.0, 0.0, 0.0, opacity * frameOpacity); |
|
#endif |
|
|
|
m_unstyledTexture->bind(); |
|
const QPoint pt = m_effectFrame->geometry().topLeft(); |
|
if (sceneShader) { |
|
shader->setUniform(GLShader::Offset, QVector2D(pt.x(), pt.y())); |
|
} else { |
|
QMatrix4x4 translation; |
|
translation.translate(pt.x(), pt.y()); |
|
if (shader) { |
|
shader->setUniform(GLShader::WindowTransformation, translation); |
|
} else { |
|
pushMatrix(translation); |
|
} |
|
} |
|
m_unstyledVBO->render(region, GL_TRIANGLES); |
|
if (!sceneShader) { |
|
if (shader) { |
|
shader->setUniform(GLShader::WindowTransformation, QMatrix4x4()); |
|
} else { |
|
popMatrix(); |
|
} |
|
} |
|
m_unstyledTexture->unbind(); |
|
} else if (m_effectFrame->style() == EffectFrameStyled) { |
|
if (!m_texture) // Lazy creation |
|
updateTexture(); |
|
|
|
if (shader) { |
|
const float a = opacity * frameOpacity; |
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
else |
|
glColor4f(1.0, 1.0, 1.0, opacity * frameOpacity); |
|
#endif |
|
m_texture->bind(); |
|
qreal left, top, right, bottom; |
|
m_effectFrame->frame().getMargins(left, top, right, bottom); // m_geometry is the inner geometry |
|
m_texture->render(region, m_effectFrame->geometry().adjusted(-left, -top, right, bottom)); |
|
m_texture->unbind(); |
|
|
|
} |
|
if (!m_effectFrame->selection().isNull()) { |
|
if (!m_selectionTexture) { // Lazy creation |
|
QPixmap pixmap = m_effectFrame->selectionFrame().framePixmap(); |
|
if (!pixmap.isNull()) |
|
m_selectionTexture = new Texture(pixmap); |
|
} |
|
if (m_selectionTexture) { |
|
if (shader) { |
|
const float a = opacity * frameOpacity; |
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
else |
|
glColor4f(1.0, 1.0, 1.0, opacity * frameOpacity); |
|
#endif |
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
|
m_selectionTexture->bind(); |
|
m_selectionTexture->render(region, m_effectFrame->selection()); |
|
m_selectionTexture->unbind(); |
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
|
} |
|
} |
|
|
|
// Render icon |
|
if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) { |
|
QPoint topLeft(m_effectFrame->geometry().x(), |
|
m_effectFrame->geometry().center().y() - m_effectFrame->iconSize().height() / 2); |
|
|
|
if (m_effectFrame->isCrossFade() && m_oldIconTexture) { |
|
if (shader) { |
|
const float a = opacity * (1.0 - m_effectFrame->crossFadeProgress()); |
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
else |
|
glColor4f(1.0, 1.0, 1.0, opacity * (1.0 - m_effectFrame->crossFadeProgress())); |
|
#endif |
|
|
|
m_oldIconTexture->bind(); |
|
m_oldIconTexture->render(region, QRect(topLeft, m_effectFrame->iconSize())); |
|
m_oldIconTexture->unbind(); |
|
if (shader) { |
|
const float a = opacity * m_effectFrame->crossFadeProgress(); |
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
else |
|
glColor4f(1.0, 1.0, 1.0, opacity * m_effectFrame->crossFadeProgress()); |
|
#endif |
|
} else { |
|
if (shader) { |
|
const QVector4D constant(opacity, opacity, opacity, opacity); |
|
shader->setUniform(GLShader::ModulationConstant, constant); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
else |
|
glColor4f(1.0, 1.0, 1.0, opacity); |
|
#endif |
|
} |
|
|
|
if (!m_iconTexture) { // lazy creation |
|
m_iconTexture = new Texture(m_effectFrame->icon()); |
|
} |
|
m_iconTexture->bind(); |
|
m_iconTexture->render(region, QRect(topLeft, m_effectFrame->iconSize())); |
|
m_iconTexture->unbind(); |
|
} |
|
|
|
// Render text |
|
if (!m_effectFrame->text().isEmpty()) { |
|
if (m_effectFrame->isCrossFade() && m_oldTextTexture) { |
|
if (shader) { |
|
const float a = opacity * (1.0 - m_effectFrame->crossFadeProgress()); |
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
else |
|
glColor4f(1.0, 1.0, 1.0, opacity *(1.0 - m_effectFrame->crossFadeProgress())); |
|
#endif |
|
|
|
m_oldTextTexture->bind(); |
|
m_oldTextTexture->render(region, m_effectFrame->geometry()); |
|
m_oldTextTexture->unbind(); |
|
if (shader) { |
|
const float a = opacity * m_effectFrame->crossFadeProgress(); |
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
else |
|
glColor4f(1.0, 1.0, 1.0, opacity * m_effectFrame->crossFadeProgress()); |
|
#endif |
|
} else { |
|
if (shader) { |
|
const QVector4D constant(opacity, opacity, opacity, opacity); |
|
shader->setUniform(GLShader::ModulationConstant, constant); |
|
} |
|
#ifndef KWIN_HAVE_OPENGLES |
|
else |
|
glColor4f(1.0, 1.0, 1.0, opacity); |
|
#endif |
|
} |
|
if (!m_textTexture) // Lazy creation |
|
updateTextTexture(); |
|
m_textTexture->bind(); |
|
m_textTexture->render(region, m_effectFrame->geometry()); |
|
m_textTexture->unbind(); |
|
} |
|
|
|
if (shader) { |
|
ShaderManager::instance()->popShader(); |
|
} |
|
glDisable(GL_BLEND); |
|
#ifndef KWIN_HAVE_OPENGLES |
|
glPopMatrix(); |
|
glPopAttrib(); |
|
#endif |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::updateTexture() |
|
{ |
|
delete m_texture; |
|
m_texture = 0L; |
|
if (m_effectFrame->style() == EffectFrameStyled) { |
|
QPixmap pixmap = m_effectFrame->frame().framePixmap(); |
|
m_texture = new Texture(pixmap); |
|
} |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::updateTextTexture() |
|
{ |
|
delete m_textTexture; |
|
m_textTexture = 0L; |
|
delete m_textPixmap; |
|
m_textPixmap = 0L; |
|
|
|
if (m_effectFrame->text().isEmpty()) |
|
return; |
|
|
|
// Determine position on texture to paint text |
|
QRect rect(QPoint(0, 0), m_effectFrame->geometry().size()); |
|
if (!m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty()) |
|
rect.setLeft(m_effectFrame->iconSize().width()); |
|
|
|
// If static size elide text as required |
|
QString text = m_effectFrame->text(); |
|
if (m_effectFrame->isStatic()) { |
|
QFontMetrics metrics(m_effectFrame->font()); |
|
text = metrics.elidedText(text, Qt::ElideRight, rect.width()); |
|
} |
|
|
|
m_textPixmap = new QPixmap(m_effectFrame->geometry().size()); |
|
m_textPixmap->fill(Qt::transparent); |
|
QPainter p(m_textPixmap); |
|
p.setFont(m_effectFrame->font()); |
|
if (m_effectFrame->style() == EffectFrameStyled) |
|
p.setPen(m_effectFrame->styledTextColor()); |
|
else // TODO: What about no frame? Custom color setting required |
|
p.setPen(Qt::white); |
|
p.drawText(rect, m_effectFrame->alignment(), text); |
|
p.end(); |
|
m_textTexture = new Texture(*m_textPixmap); |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::updateUnstyledTexture() |
|
{ |
|
delete m_unstyledTexture; |
|
m_unstyledTexture = 0L; |
|
delete m_unstyledPixmap; |
|
m_unstyledPixmap = 0L; |
|
// Based off circle() from kwinxrenderutils.cpp |
|
#define CS 8 |
|
m_unstyledPixmap = new QPixmap(2 * CS, 2 * CS); |
|
m_unstyledPixmap->fill(Qt::transparent); |
|
QPainter p(m_unstyledPixmap); |
|
p.setRenderHint(QPainter::Antialiasing); |
|
p.setPen(Qt::NoPen); |
|
p.setBrush(Qt::black); |
|
p.drawEllipse(m_unstyledPixmap->rect()); |
|
p.end(); |
|
#undef CS |
|
m_unstyledTexture = new Texture(*m_unstyledPixmap); |
|
} |
|
|
|
void SceneOpenGL::EffectFrame::cleanup() |
|
{ |
|
delete m_unstyledTexture; |
|
m_unstyledTexture = NULL; |
|
delete m_unstyledPixmap; |
|
m_unstyledPixmap = NULL; |
|
} |
|
|
|
//**************************************** |
|
// SceneOpenGL::Shadow |
|
//**************************************** |
|
SceneOpenGLShadow::SceneOpenGLShadow(Toplevel *toplevel) |
|
: Shadow(toplevel) |
|
, m_texture(NULL) |
|
{ |
|
} |
|
|
|
SceneOpenGLShadow::~SceneOpenGLShadow() |
|
{ |
|
delete m_texture; |
|
} |
|
|
|
void SceneOpenGLShadow::buildQuads() |
|
{ |
|
// prepare window quads |
|
m_shadowQuads.clear(); |
|
const QSizeF top(shadowPixmap(ShadowElementTop).size()); |
|
const QSizeF topRight(shadowPixmap(ShadowElementTopRight).size()); |
|
const QSizeF right(shadowPixmap(ShadowElementRight).size()); |
|
const QSizeF bottomRight(shadowPixmap(ShadowElementBottomRight).size()); |
|
const QSizeF bottom(shadowPixmap(ShadowElementBottom).size()); |
|
const QSizeF bottomLeft(shadowPixmap(ShadowElementBottomLeft).size()); |
|
const QSizeF left(shadowPixmap(ShadowElementLeft).size()); |
|
const QSizeF topLeft(shadowPixmap(ShadowElementTopLeft).size()); |
|
if ((left.width() - leftOffset() > topLevel()->width()) || |
|
(right.width() - rightOffset() > topLevel()->width()) || |
|
(top.height() - topOffset() > topLevel()->height()) || |
|
(bottom.height() - bottomOffset() > topLevel()->height())) { |
|
// if our shadow is bigger than the window, we don't render the shadow |
|
setShadowRegion(QRegion()); |
|
return; |
|
} |
|
|
|
const QRectF outerRect(QPointF(-leftOffset(), -topOffset()), |
|
QPointF(topLevel()->width() + rightOffset(), topLevel()->height() + bottomOffset())); |
|
|
|
const qreal width = topLeft.width() + top.width() + topRight.width(); |
|
const qreal height = topLeft.height() + left.height() + bottomLeft.height(); |
|
|
|
qreal tx1(0.0), tx2(0.0), ty1(0.0), ty2(0.0); |
|
|
|
tx2 = topLeft.width()/width; |
|
ty2 = topLeft.height()/height; |
|
WindowQuad topLeftQuad(WindowQuadShadowTopLeft); |
|
topLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y(), tx1, ty1); |
|
topLeftQuad[ 1 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), tx2, ty1); |
|
topLeftQuad[ 2 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + topLeft.height(), tx2, ty2); |
|
topLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), tx1, ty2); |
|
m_shadowQuads.append(topLeftQuad); |
|
|
|
tx1 = tx2; |
|
tx2 = (topLeft.width() + top.width())/width; |
|
ty2 = top.height()/height; |
|
WindowQuad topQuad(WindowQuadShadowTop); |
|
topQuad[ 0 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y(), tx1, ty1); |
|
topQuad[ 1 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), tx2, ty1); |
|
topQuad[ 2 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + top.height(),tx2, ty2); |
|
topQuad[ 3 ] = WindowVertex(outerRect.x() + topLeft.width(), outerRect.y() + top.height(), tx1, ty2); |
|
m_shadowQuads.append(topQuad); |
|
|
|
tx1 = tx2; |
|
tx2 = 1.0; |
|
ty2 = topRight.height()/height; |
|
WindowQuad topRightQuad(WindowQuadShadowTopRight); |
|
topRightQuad[ 0 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y(), tx1, ty1); |
|
topRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y(), tx2, ty1); |
|
topRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), tx2, ty2); |
|
topRightQuad[ 3 ] = WindowVertex(outerRect.right() - topRight.width(), outerRect.y() + topRight.height(), tx1, ty2); |
|
m_shadowQuads.append(topRightQuad); |
|
|
|
tx1 = (width - right.width())/width; |
|
ty1 = topRight.height()/height; |
|
ty2 = (topRight.height() + right.height())/height; |
|
WindowQuad rightQuad(WindowQuadShadowRight); |
|
rightQuad[ 0 ] = WindowVertex(outerRect.right() - right.width(), outerRect.y() + topRight.height(), tx1, ty1); |
|
rightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.y() + topRight.height(), tx2, ty1); |
|
rightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), tx2, ty2); |
|
rightQuad[ 3 ] = WindowVertex(outerRect.right() - right.width(), outerRect.bottom() - bottomRight.height(), tx1, ty2); |
|
m_shadowQuads.append(rightQuad); |
|
|
|
tx1 = (width - bottomRight.width())/width; |
|
ty1 = ty2; |
|
ty2 = 1.0; |
|
WindowQuad bottomRightQuad(WindowQuadShadowBottomRight); |
|
bottomRightQuad[ 0 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottomRight.height(), tx1, ty1); |
|
bottomRightQuad[ 1 ] = WindowVertex(outerRect.right(), outerRect.bottom() - bottomRight.height(), tx2, ty1); |
|
bottomRightQuad[ 2 ] = WindowVertex(outerRect.right(), outerRect.bottom(), tx2, ty2); |
|
bottomRightQuad[ 3 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), tx1, ty2); |
|
m_shadowQuads.append(bottomRightQuad); |
|
|
|
tx2 = tx1; |
|
tx1 = bottomLeft.width()/width; |
|
ty1 = (height - bottom.height())/height; |
|
WindowQuad bottomQuad(WindowQuadShadowBottom); |
|
bottomQuad[ 0 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottom.height(), tx1, ty1); |
|
bottomQuad[ 1 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom() - bottom.height(), tx2, ty1); |
|
bottomQuad[ 2 ] = WindowVertex(outerRect.right() - bottomRight.width(), outerRect.bottom(), tx2, ty2); |
|
bottomQuad[ 3 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), tx1, ty2); |
|
m_shadowQuads.append(bottomQuad); |
|
|
|
tx1 = 0.0; |
|
tx2 = bottomLeft.width()/width; |
|
ty1 = (height - bottomLeft.height())/height; |
|
WindowQuad bottomLeftQuad(WindowQuadShadowBottomLeft); |
|
bottomLeftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), tx1, ty1); |
|
bottomLeftQuad[ 1 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom() - bottomLeft.height(), tx2, ty1); |
|
bottomLeftQuad[ 2 ] = WindowVertex(outerRect.x() + bottomLeft.width(), outerRect.bottom(), tx2, ty2); |
|
bottomLeftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom(), tx1, ty2); |
|
m_shadowQuads.append(bottomLeftQuad); |
|
|
|
tx2 = left.width()/width; |
|
ty2 = ty1; |
|
ty1 = topLeft.height()/height; |
|
WindowQuad leftQuad(WindowQuadShadowLeft); |
|
leftQuad[ 0 ] = WindowVertex(outerRect.x(), outerRect.y() + topLeft.height(), tx1, ty1); |
|
leftQuad[ 1 ] = WindowVertex(outerRect.x() + left.width(), outerRect.y() + topLeft.height(), tx2, ty1); |
|
leftQuad[ 2 ] = WindowVertex(outerRect.x() + left.width(), outerRect.bottom() - bottomLeft.height(), tx2, ty2); |
|
leftQuad[ 3 ] = WindowVertex(outerRect.x(), outerRect.bottom() - bottomLeft.height(), tx1, ty2); |
|
m_shadowQuads.append(leftQuad); |
|
} |
|
|
|
bool SceneOpenGLShadow::prepareBackend() |
|
{ |
|
const QSize top(shadowPixmap(ShadowElementTop).size()); |
|
const QSize topRight(shadowPixmap(ShadowElementTopRight).size()); |
|
const QSize right(shadowPixmap(ShadowElementRight).size()); |
|
const QSize bottomRight(shadowPixmap(ShadowElementBottomRight).size()); |
|
const QSize bottom(shadowPixmap(ShadowElementBottom).size()); |
|
const QSize bottomLeft(shadowPixmap(ShadowElementBottomLeft).size()); |
|
const QSize left(shadowPixmap(ShadowElementLeft).size()); |
|
const QSize topLeft(shadowPixmap(ShadowElementTopLeft).size()); |
|
|
|
const int width = topLeft.width() + top.width() + topRight.width(); |
|
const int height = topLeft.height() + left.height() + bottomLeft.height(); |
|
|
|
QImage image(width, height, QImage::Format_ARGB32); |
|
image.fill(Qt::transparent); |
|
QPainter p; |
|
p.begin(&image); |
|
p.drawPixmap(0, 0, shadowPixmap(ShadowElementTopLeft)); |
|
p.drawPixmap(topLeft.width(), 0, shadowPixmap(ShadowElementTop)); |
|
p.drawPixmap(topLeft.width() + top.width(), 0, shadowPixmap(ShadowElementTopRight)); |
|
p.drawPixmap(0, topLeft.height(), shadowPixmap(ShadowElementLeft)); |
|
p.drawPixmap(width - right.width(), topRight.height(), shadowPixmap(ShadowElementRight)); |
|
p.drawPixmap(0, topLeft.height() + left.height(), shadowPixmap(ShadowElementBottomLeft)); |
|
p.drawPixmap(bottomLeft.width(), height - bottom.height(), shadowPixmap(ShadowElementBottom)); |
|
p.drawPixmap(bottomLeft.width() + bottom.width(), topRight.height() + right.height(), shadowPixmap(ShadowElementBottomRight)); |
|
p.end(); |
|
|
|
delete m_texture; |
|
m_texture = new GLTexture(image); |
|
|
|
return true; |
|
} |
|
|
|
} // namespace
|
|
|