virtual GLTexture createTexture(const QString& fileName) const { if (m_texMan != NULL) { return m_texMan->createTexture(fileName); } else { return GLTexture(); } }
void BlurEffect::doCachedBlur(EffectWindow *w, const QRegion& region, const float opacity) { const QRect screen = effects->virtualScreenGeometry(); const QRegion blurredRegion = blurRegion(w).translated(w->pos()) & screen; const QRegion expanded = expand(blurredRegion) & screen; const QRect r = expanded.boundingRect(); // The background texture we get is only partially valid. CacheEntry it = windows.find(w); if (it == windows.end()) { BlurWindowInfo bwi; bwi.blurredBackground = GLTexture(r.width(),r.height()); bwi.damagedRegion = expanded; bwi.dropCache = false; bwi.windowPos = w->pos(); it = windows.insert(w, bwi); } else if (it->blurredBackground.size() != r.size()) { it->blurredBackground = GLTexture(r.width(),r.height()); it->dropCache = false; it->windowPos = w->pos(); } else if (it->windowPos != w->pos()) { it->dropCache = false; it->windowPos = w->pos(); } GLTexture targetTexture = it->blurredBackground; targetTexture.setFilter(GL_LINEAR); targetTexture.setWrapMode(GL_CLAMP_TO_EDGE); shader->bind(); QMatrix4x4 textureMatrix; QMatrix4x4 modelViewProjectionMatrix; /** * Which part of the background texture can be updated ? * * Well this is a rather difficult question. We kind of rely on the fact, that * we need a bigger background region being painted before, more precisely if we want to * blur region A we need the background region expand(A). This business logic is basically * done in prePaintWindow: * data.paint |= expand(damagedArea); * * Now "data.paint" gets clipped and becomes what we receive as the "region" variable * in this function. In theory there is now only one function that does this clipping * and this is paintSimpleScreen. The clipping has the effect that "damagedRegion" * is no longer a subset of "region" and we cannot fully validate the cache within one * rendering pass. If we would now update the "damageRegion & region" part of the cache * we would wrongly update the part of the cache that is next to the "region" border and * which lies within "damagedRegion", just because we cannot assume that the framebuffer * outside of "region" is valid. Therefore the maximal damaged region of the cache that can * be repainted is given by: * validUpdate = damagedRegion - expand(damagedRegion - region); * * Now you may ask what is with the rest of "damagedRegion & region" that is not part * of "validUpdate" but also might end up on the screen. Well under the assumption * that only the occlusion culling can shrink "data.paint", we can control this by reducing * the opaque area of every window by a margin of the blurring radius (c.f. prePaintWindow). * This way we are sure that this area is overpainted by a higher opaque window. * * Apparently paintSimpleScreen is not the only function that can influence "region". * In fact every effect's paintWindow that is called before Blur::paintWindow * can do so (e.g. SlidingPopups). Hence we have to make the compromise that we update * "damagedRegion & region" of the cache but only mark "validUpdate" as valid. **/ const QRegion damagedRegion = it->damagedRegion; const QRegion updateBackground = damagedRegion & region; const QRegion validUpdate = damagedRegion - expand(damagedRegion - region); const QRegion horizontal = validUpdate.isEmpty() ? QRegion() : (updateBackground & screen); const QRegion vertical = blurredRegion & region; const int horizontalOffset = 0; const int horizontalCount = horizontal.rectCount() * 6; const int verticalOffset = horizontalCount; const int verticalCount = vertical.rectCount() * 6; GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); uploadGeometry(vbo, horizontal, vertical); vbo->bindArrays(); if (!validUpdate.isEmpty()) { const QRect updateRect = (expand(updateBackground) & expanded).boundingRect(); // First we have to copy the background from the frontbuffer // into a scratch texture (in this case "tex"). tex.bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, updateRect.x(), displayHeight() - updateRect.y() - updateRect.height(), updateRect.width(), updateRect.height()); // Draw the texture on the offscreen framebuffer object, while blurring it horizontally target->attachTexture(targetTexture); GLRenderTarget::pushRenderTarget(target); shader->setDirection(Qt::Horizontal); shader->setPixelDistance(1.0 / tex.width()); modelViewProjectionMatrix.ortho(0, r.width(), r.height(), 0 , 0, 65535); modelViewProjectionMatrix.translate(-r.x(), -r.y(), 0); shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); // Set up the texture matrix to transform from screen coordinates // to texture coordinates. textureMatrix.scale(1.0 / tex.width(), -1.0 / tex.height(), 1); textureMatrix.translate(-updateRect.x(), -updateRect.height() - updateRect.y(), 0); shader->setTextureMatrix(textureMatrix); vbo->draw(GL_TRIANGLES, horizontalOffset, horizontalCount); GLRenderTarget::popRenderTarget(); tex.unbind(); // mark the updated region as valid it->damagedRegion -= validUpdate; } // Now draw the horizontally blurred area back to the backbuffer, while // blurring it vertically and clipping it to the window shape. targetTexture.bind(); shader->setDirection(Qt::Vertical); shader->setPixelDistance(1.0 / targetTexture.height()); // Modulate the blurred texture with the window opacity if the window isn't opaque if (opacity < 1.0) { glEnable(GL_BLEND); glBlendColor(0, 0, 0, opacity); glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); } modelViewProjectionMatrix.setToIdentity(); const QSize screenSize = effects->virtualScreenSize(); modelViewProjectionMatrix.ortho(0, screenSize.width(), screenSize.height(), 0, 0, 65535); shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); // Set the up the texture matrix to transform from screen coordinates // to texture coordinates. textureMatrix.setToIdentity(); textureMatrix.scale(1.0 / targetTexture.width(), -1.0 / targetTexture.height(), 1); textureMatrix.translate(-r.x(), -targetTexture.height() - r.y(), 0); shader->setTextureMatrix(textureMatrix); vbo->draw(GL_TRIANGLES, verticalOffset, verticalCount); vbo->unbindArrays(); if (opacity < 1.0) { glDisable(GL_BLEND); } targetTexture.unbind(); shader->unbind(); }