1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsgpainternode_p.h"
44 #include <QtQuick/private/qquickpainteditem_p.h>
46 #include <QtQuick/private/qsgcontext_p.h>
47 #include <private/qopenglextensions_p.h>
48 #include <qopenglframebufferobject.h>
49 #include <qopenglfunctions.h>
50 #include <qopenglpaintdevice.h>
56 #define QT_MINIMUM_DYNAMIC_FBO_SIZE 64
58 static inline int qt_next_power_of_two(int v)
70 QSGPainterTexture::QSGPainterTexture()
77 extern void qsg_swizzleBGRAToRGBA(QImage *image);
80 void QSGPainterTexture::bind()
82 if (m_dirty_rect.isNull()) {
83 QSGPlainTexture::bind();
87 bool oldMipmapsGenerated = m_mipmaps_generated;
88 m_mipmaps_generated = true;
89 QSGPlainTexture::bind();
90 m_mipmaps_generated = oldMipmapsGenerated;
92 QImage subImage = m_image.copy(m_dirty_rect);
94 int w = m_dirty_rect.width();
95 int h = m_dirty_rect.height();
98 qsg_swizzleBGRAToRGBA(&subImage);
99 glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h,
100 GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits());
102 glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h,
103 GL_BGRA, GL_UNSIGNED_BYTE, subImage.constBits());
106 if (m_has_mipmaps && !m_mipmaps_generated) {
107 QOpenGLContext *ctx = QOpenGLContext::currentContext();
108 ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
109 m_mipmaps_generated = true;
112 m_dirty_texture = false;
113 m_dirty_bind_options = false;
115 m_dirty_rect = QRect();
118 QSGPainterNode::QSGPainterNode(QQuickPaintedItem *item)
120 , m_preferredRenderTarget(QQuickPaintedItem::Image)
121 , m_actualRenderTarget(QQuickPaintedItem::Image)
124 , m_multisampledFbo(0)
125 , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
129 , m_dirtyContents(false)
130 , m_opaquePainting(false)
131 , m_linear_filtering(false)
132 , m_mipmapping(false)
133 , m_smoothPainting(false)
134 , m_extensionsChecked(false)
135 , m_multisamplingSupported(false)
136 , m_fastFBOResizing(false)
137 , m_fillColor(Qt::transparent)
138 , m_contentsScale(1.0)
139 , m_dirtyGeometry(false)
140 , m_dirtyRenderTarget(false)
141 , m_dirtyTexture(false)
143 m_context = static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphContext();
145 setMaterial(&m_materialO);
146 setOpaqueMaterial(&m_material);
147 setGeometry(&m_geometry);
150 QSGPainterNode::~QSGPainterNode()
154 delete m_multisampledFbo;
158 void QSGPainterNode::paint()
160 QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect;
163 if (m_actualRenderTarget == QQuickPaintedItem::Image) {
164 if (m_image.isNull())
166 painter.begin(&m_image);
169 m_gl_device = new QOpenGLPaintDevice(m_fboSize);
170 m_gl_device->setPaintFlipped(true);
173 if (m_multisampledFbo)
174 m_multisampledFbo->bind();
178 painter.begin(m_gl_device);
181 if (m_smoothPainting) {
182 painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing
183 | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
186 painter.scale(m_contentsScale, m_contentsScale);
188 QRect sclip(qFloor(dirtyRect.x()/m_contentsScale),
189 qFloor(dirtyRect.y()/m_contentsScale),
190 qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)),
191 qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale)));
193 if (!m_dirtyRect.isNull())
194 painter.setClipRect(sclip);
196 painter.setCompositionMode(QPainter::CompositionMode_Source);
197 painter.fillRect(sclip, m_fillColor);
198 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
200 m_item->paint(&painter);
203 if (m_actualRenderTarget == QQuickPaintedItem::Image) {
204 m_texture->setImage(m_image);
205 m_texture->setDirtyRect(dirtyRect);
206 } else if (m_multisampledFbo) {
207 QOpenGLFramebufferObject::blitFramebuffer(m_fbo, dirtyRect, m_multisampledFbo, dirtyRect);
210 if (m_multisampledFbo)
211 m_multisampledFbo->release();
215 m_dirtyRect = QRect();
218 void QSGPainterNode::update()
220 if (m_dirtyRenderTarget)
221 updateRenderTarget();
230 m_dirtyGeometry = false;
231 m_dirtyRenderTarget = false;
232 m_dirtyTexture = false;
233 m_dirtyContents = false;
236 void QSGPainterNode::updateTexture()
238 m_texture->setHasMipmaps(m_mipmapping);
239 m_texture->setHasAlphaChannel(!m_opaquePainting);
240 m_material.setTexture(m_texture);
241 m_materialO.setTexture(m_texture);
243 markDirty(DirtyMaterial);
246 void QSGPainterNode::updateGeometry()
249 if (m_actualRenderTarget == QQuickPaintedItem::Image)
250 source = QRectF(0, 0, 1, 1);
252 source = QRectF(0, 0, qreal(m_size.width()) / m_fboSize.width(), qreal(m_size.height()) / m_fboSize.height());
253 QRectF dest(0, 0, m_size.width(), m_size.height());
254 if (m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
255 dest = QRectF(QPointF(0, m_size.height()), QPointF(m_size.width(), 0));
256 QSGGeometry::updateTexturedRectGeometry(&m_geometry,
259 markDirty(DirtyGeometry);
262 void QSGPainterNode::updateRenderTarget()
264 if (!m_extensionsChecked) {
265 QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
266 m_multisamplingSupported = extensions.contains("GL_EXT_framebuffer_multisample")
267 && extensions.contains("GL_EXT_framebuffer_blit");
268 m_extensionsChecked = true;
271 m_dirtyContents = true;
273 QQuickPaintedItem::RenderTarget oldTarget = m_actualRenderTarget;
274 if (m_preferredRenderTarget == QQuickPaintedItem::Image) {
275 m_actualRenderTarget = QQuickPaintedItem::Image;
277 if (!m_multisamplingSupported && m_smoothPainting)
278 m_actualRenderTarget = QQuickPaintedItem::Image;
280 m_actualRenderTarget = m_preferredRenderTarget;
282 if (oldTarget != m_actualRenderTarget) {
285 delete m_multisampledFbo;
286 m_fbo = m_multisampledFbo = 0;
289 if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject ||
290 m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject) {
291 const QOpenGLContext *ctx = m_context->glContext();
292 if (m_fbo && !m_dirtyGeometry && (!ctx->format().samples() || !m_multisamplingSupported))
295 if (m_fboSize.isEmpty())
299 delete m_multisampledFbo;
300 m_fbo = m_multisampledFbo = 0;
302 if (m_smoothPainting && ctx->format().samples() && m_multisamplingSupported) {
304 QOpenGLFramebufferObjectFormat format;
305 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
306 format.setSamples(8);
307 m_multisampledFbo = new QOpenGLFramebufferObject(m_fboSize, format);
310 QOpenGLFramebufferObjectFormat format;
311 format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
312 m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
315 QOpenGLFramebufferObjectFormat format;
316 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
317 m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
320 if (!m_image.isNull() && !m_dirtyGeometry)
323 m_image = QImage(m_size, QImage::Format_ARGB32_Premultiplied);
324 m_image.fill(Qt::transparent);
327 QSGPainterTexture *texture = new QSGPainterTexture;
328 if (m_actualRenderTarget == QQuickPaintedItem::Image) {
329 texture->setOwnsTexture(true);
330 texture->setTextureSize(m_size);
332 texture->setTextureId(m_fbo->texture());
333 texture->setOwnsTexture(false);
334 texture->setTextureSize(m_fboSize);
340 texture->setTextureSize(m_size);
344 void QSGPainterNode::updateFBOSize()
348 if (m_fastFBOResizing) {
349 fboWidth = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.width()));
350 fboHeight = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qt_next_power_of_two(m_size.height()));
352 QSize minimumFBOSize = m_context->minimumFBOSize();
353 fboWidth = qMax(minimumFBOSize.width(), m_size.width());
354 fboHeight = qMax(minimumFBOSize.height(), m_size.height());
357 m_fboSize = QSize(fboWidth, fboHeight);
360 void QSGPainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target)
362 if (m_preferredRenderTarget == target)
365 m_preferredRenderTarget = target;
367 m_dirtyRenderTarget = true;
368 m_dirtyGeometry = true;
369 m_dirtyTexture = true;
372 void QSGPainterNode::setSize(const QSize &size)
381 m_dirtyRenderTarget = m_fbo->size() != m_fboSize || m_dirtyRenderTarget;
383 m_dirtyRenderTarget = true;
384 m_dirtyGeometry = true;
385 m_dirtyTexture = true;
388 void QSGPainterNode::setDirty(const QRect &dirtyRect)
390 m_dirtyContents = true;
391 m_dirtyRect = dirtyRect;
394 m_dirtyTexture = true;
396 markDirty(DirtyMaterial);
399 void QSGPainterNode::setOpaquePainting(bool opaque)
401 if (opaque == m_opaquePainting)
404 m_opaquePainting = opaque;
405 m_dirtyTexture = true;
408 void QSGPainterNode::setLinearFiltering(bool linearFiltering)
410 if (linearFiltering == m_linear_filtering)
413 m_linear_filtering = linearFiltering;
415 m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
416 m_materialO.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
417 markDirty(DirtyMaterial);
420 void QSGPainterNode::setMipmapping(bool mipmapping)
422 if (mipmapping == m_mipmapping)
425 m_mipmapping = mipmapping;
426 m_material.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
427 m_materialO.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
428 m_dirtyTexture = true;
431 void QSGPainterNode::setSmoothPainting(bool s)
433 if (s == m_smoothPainting)
436 m_smoothPainting = s;
437 m_dirtyRenderTarget = true;
440 void QSGPainterNode::setFillColor(const QColor &c)
442 if (c == m_fillColor)
446 markDirty(DirtyMaterial);
449 void QSGPainterNode::setContentsScale(qreal s)
451 if (s == m_contentsScale)
455 markDirty(DirtyMaterial);
458 void QSGPainterNode::setFastFBOResizing(bool dynamic)
460 m_fastFBOResizing = dynamic;
463 QImage QSGPainterNode::toImage() const
465 if (m_actualRenderTarget == QQuickPaintedItem::Image)
468 return m_fbo->toImage();