Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qsgsoftwarerenderablenode_p.h"
++
++ #include "qsgsoftwareinternalimagenode_p.h"
++ #include "qsgsoftwareinternalrectanglenode_p.h"
++ #include "qsgsoftwareglyphnode_p.h"
++ #include "qsgsoftwarepublicnodes_p.h"
++ #include "qsgsoftwarepainternode_p.h"
++ #include "qsgsoftwarepixmaptexture_p.h"
++ #if QT_CONFIG(quick_sprite)
++ #include "qsgsoftwarespritenode_p.h"
++ #endif
++
++ #include <qsgsimplerectnode.h>
++ #include <qsgsimpletexturenode.h>
++ #include <private/qsgrendernode_p.h>
++ #include <private/qsgplaintexture_p.h>
++
++ #include <qmath.h>
++
++ Q_STATIC_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable")
++
++ QT_BEGIN_NAMESPACE
++
++ // Largest subrectangle with integer coordinates
++ inline QRect toRectMin(const QRectF & r)
++ {
++ int x1 = qCeil(r.left());
++ int x2 = qFloor(r.right());
++ int y1 = qCeil(r.top());
++ int y2 = qFloor(r.bottom());
++ return QRect(x1, y1, x2 - x1, y2 - y1);
++ }
++
++ // Smallest superrectangle with integer coordinates
++ inline QRect toRectMax(const QRectF & r)
++ {
++ return r.toAlignedRect();
++ }
++
++ QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *node)
++ : m_nodeType(type)
++ , m_isOpaque(true)
++ , m_isDirty(true)
++ , m_hasClipRegion(false)
++ , m_opacity(1.0f)
++ {
++ switch (m_nodeType) {
++ case QSGSoftwareRenderableNode::SimpleRect:
++ m_handle.simpleRectNode = static_cast<QSGSimpleRectNode*>(node);
++ break;
++ case QSGSoftwareRenderableNode::SimpleTexture:
++ m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node);
++ break;
++ case QSGSoftwareRenderableNode::Image:
++ m_handle.imageNode = static_cast<QSGSoftwareInternalImageNode*>(node);
++ break;
++ case QSGSoftwareRenderableNode::Painter:
++ m_handle.painterNode = static_cast<QSGSoftwarePainterNode*>(node);
++ break;
++ case QSGSoftwareRenderableNode::Rectangle:
++ m_handle.rectangleNode = static_cast<QSGSoftwareInternalRectangleNode*>(node);
++ break;
++ case QSGSoftwareRenderableNode::Glyph:
++ m_handle.glpyhNode = static_cast<QSGSoftwareGlyphNode*>(node);
++ break;
++ case QSGSoftwareRenderableNode::NinePatch:
++ m_handle.ninePatchNode = static_cast<QSGSoftwareNinePatchNode*>(node);
++ break;
++ case QSGSoftwareRenderableNode::SimpleRectangle:
++ m_handle.simpleRectangleNode = static_cast<QSGRectangleNode*>(node);
++ break;
++ case QSGSoftwareRenderableNode::SimpleImage:
++ m_handle.simpleImageNode = static_cast<QSGImageNode*>(node);
++ break;
++ #if QT_CONFIG(quick_sprite)
++ case QSGSoftwareRenderableNode::SpriteNode:
++ m_handle.spriteNode = static_cast<QSGSoftwareSpriteNode*>(node);
++ break;
++ #endif
++ case QSGSoftwareRenderableNode::RenderNode:
++ m_handle.renderNode = static_cast<QSGRenderNode*>(node);
++ break;
++ case QSGSoftwareRenderableNode::Invalid:
++ m_handle.simpleRectNode = nullptr;
++ break;
++ }
++ }
++
++ QSGSoftwareRenderableNode::~QSGSoftwareRenderableNode()
++ {
++
++ }
++
++ void QSGSoftwareRenderableNode::update()
++ {
++ // Update the Node properties
++ m_isDirty = true;
++ m_isOpaque = false;
++
++ QRectF boundingRect;
++
++ switch (m_nodeType) {
++ case QSGSoftwareRenderableNode::SimpleRect:
++ if (m_handle.simpleRectNode->color().alpha() == 255)
++ m_isOpaque = true;
++
++ boundingRect = m_handle.simpleRectNode->rect();
++ break;
++ case QSGSoftwareRenderableNode::SimpleTexture:
++ if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel())
++ m_isOpaque = true;
++
++ boundingRect = m_handle.simpleTextureNode->rect();
++ break;
++ case QSGSoftwareRenderableNode::Image:
++ m_isOpaque = !m_handle.imageNode->pixmap().hasAlphaChannel();
++
++ boundingRect = m_handle.imageNode->rect().toRect();
++ break;
++ case QSGSoftwareRenderableNode::Painter:
++ if (m_handle.painterNode->opaquePainting())
++ m_isOpaque = true;
++
++ boundingRect = QRectF(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height());
++ break;
++ case QSGSoftwareRenderableNode::Rectangle:
++ if (m_handle.rectangleNode->isOpaque())
++ m_isOpaque = true;
++
++ boundingRect = m_handle.rectangleNode->rect();
++ break;
++ case QSGSoftwareRenderableNode::Glyph:
++ // Always has alpha
++ boundingRect = m_handle.glpyhNode->boundingRect();
++ break;
++ case QSGSoftwareRenderableNode::NinePatch:
++ m_isOpaque = m_handle.ninePatchNode->isOpaque();
++
++ boundingRect = m_handle.ninePatchNode->bounds();
++ break;
++ case QSGSoftwareRenderableNode::SimpleRectangle:
++ if (m_handle.simpleRectangleNode->color().alpha() == 255)
++ m_isOpaque = true;
++
++ boundingRect = m_handle.simpleRectangleNode->rect();
++ break;
++ case QSGSoftwareRenderableNode::SimpleImage:
++ if (!m_handle.simpleImageNode->texture()->hasAlphaChannel())
++ m_isOpaque = true;
++
++ boundingRect = m_handle.simpleImageNode->rect();
++ break;
++ #if QT_CONFIG(quick_sprite)
++ case QSGSoftwareRenderableNode::SpriteNode:
++ m_isOpaque = m_handle.spriteNode->isOpaque();
++ boundingRect = m_handle.spriteNode->rect();
++ break;
++ #endif
++ case QSGSoftwareRenderableNode::RenderNode:
++ if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering))
++ m_isOpaque = true;
++
++ boundingRect = m_handle.renderNode->rect();
++ break;
++ default:
++ break;
++ }
++
++ if (m_transform.isRotating())
++ m_isOpaque = false;
++
++ const QRectF transformedRect = m_transform.mapRect(boundingRect);
++ m_boundingRectMin = toRectMin(transformedRect);
++ m_boundingRectMax = toRectMax(transformedRect);
++
++ if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) {
++ // If there is a clipRegion, and it is empty, the item wont be rendered
++ if (m_clipRegion.isEmpty()) {
++ m_boundingRectMin = QRect();
++ m_boundingRectMax = QRect();
++ } else {
++ const auto rects = m_clipRegion.begin();
++ m_boundingRectMin = m_boundingRectMin.intersected(rects[0]);
++ m_boundingRectMax = m_boundingRectMax.intersected(rects[0]);
++ }
++ }
++
++ // Overrides
++ if (m_opacity < 1.0f)
++ m_isOpaque = false;
++
++ m_dirtyRegion = QRegion(m_boundingRectMax);
++ }
++
++ struct RenderNodeState : public QSGRenderNode::RenderState
++ {
++ const QMatrix4x4 *projectionMatrix() const override { return &ident; }
++ QRect scissorRect() const override { return QRect(); }
++ bool scissorEnabled() const override { return false; }
++ int stencilValue() const override { return 0; }
++ bool stencilEnabled() const override { return false; }
++ const QRegion *clipRegion() const override { return &cr; }
++ QMatrix4x4 ident;
++ QRegion cr;
++ };
++
++ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting)
++ {
++ Q_ASSERT(painter);
++
++ // Check for don't paint conditions
++ if (m_nodeType != RenderNode) {
++ if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) {
++ m_isDirty = false;
++ m_dirtyRegion = QRegion();
++ return QRegion();
++ }
++ } else {
++ if (!m_isDirty || qFuzzyIsNull(m_opacity)) {
++ m_isDirty = false;
++ m_dirtyRegion = QRegion();
++ return QRegion();
++ } else {
++ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(m_handle.renderNode);
++ rd->m_localMatrix = m_transform;
++ rd->m_matrix = &rd->m_localMatrix;
++ rd->m_opacity = m_opacity;
++
++ // all the clip region below is in world coordinates, taking m_transform into account already
++ QRegion cr = m_dirtyRegion;
++ if (m_clipRegion.rectCount() > 1)
++ cr &= m_clipRegion;
++
++ painter->save();
++ RenderNodeState rs;
++ rs.cr = cr;
++ m_handle.renderNode->render(&rs);
++ painter->restore();
++
++ const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
++ ? m_boundingRectMax // already mapped to world
++ : QRect(0, 0, painter->device()->width(), painter->device()->height());
++ m_previousDirtyRegion = QRegion(br);
++ m_isDirty = false;
++ m_dirtyRegion = QRegion();
++ return br;
++ }
++ }
++
++ painter->save();
++ painter->setOpacity(m_opacity);
++
++ // Set clipRegion to m_dirtyRegion (in world coordinates, so must be done before the setTransform below)
++ // as m_dirtyRegion already accounts for clipRegion
++ painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip);
++ if (m_clipRegion.rectCount() > 1)
++ painter->setClipRegion(m_clipRegion, Qt::IntersectClip);
++
++ painter->setTransform(m_transform, false); //precalculated worldTransform
++ if (forceOpaquePainting || m_isOpaque)
++ painter->setCompositionMode(QPainter::CompositionMode_Source);
++
++ switch (m_nodeType) {
++ case QSGSoftwareRenderableNode::SimpleRect:
++ painter->fillRect(m_handle.simpleRectNode->rect(), m_handle.simpleRectNode->color());
++ break;
++ case QSGSoftwareRenderableNode::SimpleTexture:
++ {
++ QSGTexture *texture = m_handle.simpleTextureNode->texture();
++ if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(texture)) {
++ const QPixmap &pm = pt->pixmap();
++ painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, m_handle.simpleTextureNode->sourceRect());
++ } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(texture)) {
++ const QImage &im = pt->image();
++ painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect());
++ }
++ }
++ break;
++ case QSGSoftwareRenderableNode::Image:
++ m_handle.imageNode->paint(painter);
++ break;
++ case QSGSoftwareRenderableNode::Painter:
++ m_handle.painterNode->paint(painter);
++ break;
++ case QSGSoftwareRenderableNode::Rectangle:
++ m_handle.rectangleNode->paint(painter);
++ break;
++ case QSGSoftwareRenderableNode::Glyph:
++ m_handle.glpyhNode->paint(painter);
++ break;
++ case QSGSoftwareRenderableNode::NinePatch:
++ m_handle.ninePatchNode->paint(painter);
++ break;
++ case QSGSoftwareRenderableNode::SimpleRectangle:
++ static_cast<QSGSoftwareRectangleNode *>(m_handle.simpleRectangleNode)->paint(painter);
++ break;
++ case QSGSoftwareRenderableNode::SimpleImage:
++ static_cast<QSGSoftwareImageNode *>(m_handle.simpleImageNode)->paint(painter);
++ break;
++ #if QT_CONFIG(quick_sprite)
++ case QSGSoftwareRenderableNode::SpriteNode:
++ static_cast<QSGSoftwareSpriteNode *>(m_handle.spriteNode)->paint(painter);
++ break;
++ #endif
++ default:
++ break;
++ }
++
++ painter->restore();
++
++ QRegion areaToBeFlushed = m_dirtyRegion;
++ m_previousDirtyRegion = QRegion(m_boundingRectMax);
++ m_isDirty = false;
++ m_dirtyRegion = QRegion();
++
++ return areaToBeFlushed;
++ }
++
++ bool QSGSoftwareRenderableNode::isDirtyRegionEmpty() const
++ {
++ return m_dirtyRegion.isEmpty();
++ }
++
++ void QSGSoftwareRenderableNode::setTransform(const QTransform &transform)
++ {
++ if (m_transform == transform)
++ return;
++ m_transform = transform;
++ update();
++ }
++
++ void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect, bool hasClipRegion)
++ {
++ if (m_clipRegion == clipRect && m_hasClipRegion == hasClipRegion)
++ return;
++
++ m_clipRegion = clipRect;
++ m_hasClipRegion = hasClipRegion;
++ update();
++ }
++
++ void QSGSoftwareRenderableNode::setOpacity(float opacity)
++ {
++ if (qFuzzyCompare(m_opacity, opacity))
++ return;
++
++ m_opacity = opacity;
++ update();
++ }
++
++ void QSGSoftwareRenderableNode::markGeometryDirty()
++ {
++ update();
++ }
++
++ void QSGSoftwareRenderableNode::markMaterialDirty()
++ {
++ update();
++ }
++
++ void QSGSoftwareRenderableNode::addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty)
++ {
++ // Check if the dirty region applies to this node
++ QRegion prev = m_dirtyRegion;
++ if (dirtyRegion.intersects(m_boundingRectMax)) {
++ if (forceDirty)
++ m_isDirty = true;
++ m_dirtyRegion += dirtyRegion.intersected(m_boundingRectMax);
++ }
++ qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion;
++ }
++
++ void QSGSoftwareRenderableNode::subtractDirtyRegion(const QRegion &dirtyRegion)
++ {
++ QRegion prev = m_dirtyRegion;
++ if (m_isDirty) {
++ // Check if this rect concerns us
++ if (dirtyRegion.intersects(m_boundingRectMax)) {
++ m_dirtyRegion -= dirtyRegion;
++ if (m_dirtyRegion.isEmpty())
++ m_isDirty = false;
++ }
++ }
++ qCDebug(lcRenderable) << "subtractDirtyRegion: " << dirtyRegion << "old dirtyRegion" << prev << "new dirtyRegion: " << m_dirtyRegion;
++ }
++
++ QRegion QSGSoftwareRenderableNode::previousDirtyRegion(bool wasRemoved) const
++ {
++ // When removing a node, the boundingRect shouldn't be subtracted
++ // because a deleted node has no valid boundingRect
++ if (wasRemoved)
++ return m_previousDirtyRegion;
++
++ return m_previousDirtyRegion.subtracted(QRegion(m_boundingRectMax));
++ }
++
++ QRegion QSGSoftwareRenderableNode::dirtyRegion() const
++ {
++ return m_dirtyRegion;
++ }
++
++ QT_END_NAMESPACE