summaryrefslogtreecommitdiffstats
path: root/src/imports/shaders/shadereffectsource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/imports/shaders/shadereffectsource.cpp')
-rw-r--r--src/imports/shaders/shadereffectsource.cpp451
1 files changed, 451 insertions, 0 deletions
diff --git a/src/imports/shaders/shadereffectsource.cpp b/src/imports/shaders/shadereffectsource.cpp
new file mode 100644
index 00000000..aa434e1c
--- /dev/null
+++ b/src/imports/shaders/shadereffectsource.cpp
@@ -0,0 +1,451 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QML Shaders plugin of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "shadereffectsource.h"
+#include "shadereffectbuffer.h"
+#include "shadereffect.h"
+#include "glfunctions.h"
+
+#include <QtOpenGL>
+
+/*!
+ \qmlclass ShaderEffectSource ShaderEffectSource
+ \ingroup qml-shader-elements
+ \brief The ShaderEffectSource object encapsulates the source content for the ShaderEffectItem.
+
+ ShaderEffectSource is available in the \bold{Qt.labs.shaders 1.0} module.
+ \e {Elements in the Qt.labs module are not guaranteed to remain compatible
+ in future versions.}
+
+ This element provides preliminary support for OpenGL shaders in QML,
+ and may be heavily changed or removed in later versions.
+
+ Requirement for the ability to use of shaders is that the application is either using
+ opengl graphicssystem or has set QGLWidget as the viewport to QDeclarativeView (recommended way).
+
+ ShaderEffectSource object encapsulates the source content so that it can be utilized in ShaderEffectItem.
+ Source content can be a live QML object tree, or a snapshot of QML object tree.
+
+*/
+
+ShaderEffectSource::ShaderEffectSource(QDeclarativeItem *parent)
+ : QDeclarativeItem(parent)
+ , m_sourceItem(0)
+ , m_wrapMode(ClampToEdge)
+ , m_sourceRect(0, 0, 0, 0)
+ , m_textureSize(0, 0)
+ , m_format(RGBA)
+ , m_size(0, 0)
+ , m_fbo(0)
+ , m_multisampledFbo(0)
+ , m_refs(0)
+ , m_dirtyTexture(true)
+ , m_dirtySceneGraph(true)
+ , m_multisamplingSupported(false)
+ , m_checkedForMultisamplingSupport(false)
+ , m_live(true)
+ , m_hideSource(false)
+ , m_mirrored(false)
+{
+}
+
+ShaderEffectSource::~ShaderEffectSource()
+{
+ if (m_refs && m_sourceItem)
+ detachSourceItem();
+
+ delete m_fbo;
+ delete m_multisampledFbo;
+}
+
+/*!
+ \qmlproperty Item ShaderEffectSource::sourceItem
+ This property holds the Item which is used as the source for the shader effect.
+ If the item has children, those are included as well.
+
+ \note When source item content is passed to the ShaderEffectItem(s), it is always clipped to the boundingrect of the
+ sourceItem regardless of its clipping property.
+*/
+
+void ShaderEffectSource::setSourceItem(QDeclarativeItem *item)
+{
+ if (item == m_sourceItem)
+ return;
+
+ if (m_sourceItem) {
+ disconnect(m_sourceItem, SIGNAL(widthChanged()), this, SLOT(markSourceSizeDirty()));
+ disconnect(m_sourceItem, SIGNAL(heightChanged()), this, SLOT(markSourceSizeDirty()));
+
+ if (m_refs)
+ detachSourceItem();
+ }
+
+ m_sourceItem = item;
+
+ if (m_sourceItem) {
+
+ // Must have some item as parent
+ if (m_sourceItem->parentItem() == 0)
+ m_sourceItem->setParentItem(this);
+
+ if (m_refs)
+ attachSourceItem();
+
+ connect(m_sourceItem, SIGNAL(widthChanged()), this, SLOT(markSourceSizeDirty()));
+ connect(m_sourceItem, SIGNAL(heightChanged()), this, SLOT(markSourceSizeDirty()));
+ }
+
+ updateSizeAndTexture();
+ emit sourceItemChanged();
+ emit repaintRequired();
+}
+
+/*!
+ \qmlproperty QRectF ShaderEffectSource::sourceRect
+ This property can be used to specify margins for the source content.
+
+ If other value than Qt.rect(0,0,0,0) is assigned to this property, it is interpreted as
+ specifying a relative source rectangle for the source content.
+
+ For example, setting Qt.rect(-10.0, -10.0, 120.0, 120.0) for a source that has width and height
+ of 100 pixels would produce 10 pixels margins to each side of the source.
+
+ Margins are useful when the original content is wanted to be spread outside the original source area,
+ like when creating a dropshadow with the shader or in other similar effects.
+
+ The default value is Qt.rect(0,0,0,0).
+*/
+
+void ShaderEffectSource::setSourceRect(const QRectF &rect)
+{
+ if (rect == m_sourceRect)
+ return;
+ m_sourceRect = rect;
+ updateSizeAndTexture();
+ emit sourceRectChanged();
+ emit repaintRequired();
+
+ m_dirtyTexture = true;
+ markSourceItemDirty();
+}
+
+/*!
+ \qmlproperty QSize ShaderEffectSource::textureSize
+ This property holds the size for the texture containing the source content.
+
+ If value QSize(0,0) is assigned to this property, texture is resized
+ according to the source size. Otherwise source content is scaled to
+ the given size.
+
+ The default value is QSize(0,0).
+*/
+
+void ShaderEffectSource::setTextureSize(const QSize &size)
+{
+ if (size == m_textureSize)
+ return;
+
+ m_textureSize = size;
+ updateSizeAndTexture();
+ emit textureSizeChanged();
+ emit repaintRequired();
+
+ m_dirtyTexture = true;
+ markSourceItemDirty();
+}
+
+/*!
+ \qmlproperty bool ShaderEffectSource::live
+ This property holds the optimization flag to define whether the source item content is changing or
+ static.
+
+ If value true is assigned to this property, source item content is re-rendered into a
+ texture for every frame. Setting the value to false improves the performance as it skips
+ rendering the source item (and its chidleren) and instead immediately passes the previously
+ rendered and cached texture to the shaders.
+
+ The default value is true.
+*/
+
+void ShaderEffectSource::setLive(bool s)
+{
+ if (s == m_live)
+ return;
+
+ m_live = s;
+
+ emit liveChanged();
+ emit repaintRequired();
+}
+
+/*!
+ \qmlproperty bool ShaderEffectSource::hideSource
+ This property holds the flag to define whether the original source item is
+ hidden when the effect item is drawn.
+
+ The default value is false.
+*/
+
+void ShaderEffectSource::setHideSource(bool hide)
+{
+ if (hide == m_hideSource)
+ return;
+
+ m_hideSource = hide;
+
+ emit hideSourceChanged();
+ emit repaintRequired();
+}
+
+/*!
+ \qmlproperty enumeration ShaderEffectSource::wrapMode
+
+ This property defines the wrap parameter for the source after it has been mapped as a texture.
+
+ \list
+ \o ShaderEffectSource.ClampToEdge - Causes texturecoordinates to be clamped to the range [ 1/2*N , 1 - 1/2*N ], where N is the texture width.
+ \o ShaderEffectSource.RepeatHorizontally - Causes the integer part of the horizontal texturecoordinate to be ignored; the GL uses only the fractional part, thereby creating a horizontal repeating pattern.
+ \o ShaderEffectSource.RepeatVertically - Causes the integer part of the vertical texturecoordinate to be ignored; the GL uses only the fractional part, thereby creating a vertical repeating pattern.
+ \o ShaderEffectSource.Repeat - Causes the integer part of both the horizontal and vertical texturecoordinates to be ignored; the GL uses only the fractional part, thereby creating a repeating pattern.
+ \endlist
+
+ The default value is ClampToEdge.
+
+*/
+
+void ShaderEffectSource::setWrapMode(WrapMode mode)
+{
+ if (mode == m_wrapMode)
+ return;
+
+ m_wrapMode = mode;
+ emit wrapModeChanged();
+
+ m_dirtyTexture = true;
+ markSourceItemDirty();
+}
+
+/*!
+ \qmlmethod ShaderEffectSource::grab()
+
+ Repaints the source item content into the texture.
+
+ This method is useful when ShaderEffectSource::live has been set to false and
+ the changes in the source item content is desired to be made visible for the shaders.
+
+*/
+
+void ShaderEffectSource::grab()
+{
+ m_dirtyTexture = true;
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::bind()
+{
+ GLint filtering = smooth() ? GL_LINEAR : GL_NEAREST;
+ GLuint hwrap = (m_wrapMode == Repeat || m_wrapMode == RepeatHorizontally) ? GL_REPEAT : GL_CLAMP_TO_EDGE;
+ GLuint vwrap = (m_wrapMode == Repeat || m_wrapMode == RepeatVertically) ? GL_REPEAT : GL_CLAMP_TO_EDGE;
+
+#if !defined(QT_OPENGL_ES_2)
+ glEnable(GL_TEXTURE_2D);
+#endif
+
+ if (m_fbo && m_fbo->isValid()) {
+ glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
+ } else {
+ m_dirtyTexture = true;
+ emit repaintRequired();
+ markSourceItemDirty();
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth() ? GL_LINEAR : GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, hwrap);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vwrap);
+}
+
+void ShaderEffectSource::refFromEffectItem()
+{
+ if (m_refs++ == 0) {
+ attachSourceItem();
+ emit activeChanged();
+ }
+}
+
+void ShaderEffectSource::derefFromEffectItem()
+{
+ if (--m_refs == 0) {
+ detachSourceItem();
+ emit activeChanged();
+ }
+ Q_ASSERT(m_refs >= 0);
+}
+
+void ShaderEffectSource::updateBackbuffer()
+{
+ if (!m_sourceItem || !QGLContext::currentContext())
+ return;
+
+ // Multisampling is not (for now) supported.
+ QSize size = QSize(m_sourceItem->width(), m_sourceItem->height());
+ if (!m_textureSize.isEmpty())
+ size = m_textureSize;
+
+ if (size.height() > 0 && size.width() > 0) {
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(m_format);
+
+ if (!m_fbo) {
+ m_fbo = new ShaderEffectBuffer(size, format);
+ } else {
+ if (!m_fbo->isValid() || m_fbo->size() != size || m_fbo->format().internalTextureFormat() != GLenum(m_format)) {
+ delete m_fbo;
+ m_fbo = 0;
+ m_fbo = new ShaderEffectBuffer(size, format);
+ }
+ }
+ }
+
+ // Note that real update for the source content happens in shadereffect.cpp
+ m_dirtyTexture = false;
+}
+
+void ShaderEffectSource::markSceneGraphDirty()
+{
+ m_dirtySceneGraph = true;
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::markSourceSizeDirty()
+{
+ Q_ASSERT(m_sourceItem);
+ if (m_textureSize.isEmpty())
+ updateSizeAndTexture();
+ if (m_refs)
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::markSourceItemDirty()
+{
+ m_dirtyTexture = true;
+ if (m_sourceItem) {
+ ShaderEffect* effect = qobject_cast<ShaderEffect*> (m_sourceItem->graphicsEffect());
+ if (effect)
+ effect->m_changed = true;
+ }
+}
+
+void ShaderEffectSource::updateSizeAndTexture()
+{
+ if (m_sourceItem) {
+ QSize size = m_textureSize;
+ if (size.isEmpty())
+ size = QSize(m_sourceItem->width(), m_sourceItem->height());
+ if (size.width() < 1)
+ size.setWidth(1);
+ if (size.height() < 1)
+ size.setHeight(1);
+ if (m_fbo && (m_fbo->size() != size || !m_fbo->isValid())) {
+ delete m_fbo;
+ m_fbo = 0;
+ delete m_multisampledFbo;
+ m_fbo = m_multisampledFbo = 0;
+ }
+ if (m_size.width() != size.width()) {
+ m_size.setWidth(size.width());
+ emit widthChanged();
+ }
+ if (m_size.height() != size.height()) {
+ m_size.setHeight(size.height());
+ emit heightChanged();
+ }
+ m_dirtyTexture = true;
+ } else {
+ if (m_size.width() != 0) {
+ m_size.setWidth(0);
+ emit widthChanged();
+ }
+ if (m_size.height() != 0) {
+ m_size.setHeight(0);
+ emit heightChanged();
+ }
+ }
+}
+
+void ShaderEffectSource::attachSourceItem()
+{
+ if (!m_sourceItem)
+ return;
+
+ ShaderEffect *effect = qobject_cast<ShaderEffect*> (m_sourceItem->graphicsEffect());
+
+ if (!effect) {
+ effect = new ShaderEffect();
+ m_sourceItem->setGraphicsEffect(effect);
+ }
+
+ if (effect)
+ effect->addRenderTarget(this);
+
+ m_sourceItem->update();
+}
+
+void ShaderEffectSource::detachSourceItem()
+{
+ if (!m_sourceItem)
+ return;
+
+ ShaderEffect* effect = qobject_cast<ShaderEffect*> (m_sourceItem->graphicsEffect());
+
+ if (effect)
+ effect->removeRenderTarget(this);
+
+ delete m_fbo;
+ m_fbo = 0;
+
+ delete m_multisampledFbo;
+ m_multisampledFbo = 0;
+
+ m_dirtyTexture = true;
+}