8f205040dc
Different canvas clip reference frames are handled now, and updating the clip nodes should not longer crash. Unfortunately, clips set on groups don't work yet, further work is needed here.
198 lines
5.7 KiB
C++
198 lines
5.7 KiB
C++
//
|
|
// Copyright (C) 2017 James Turner zakalawe@mac.com
|
|
//
|
|
// 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, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
#include "canvasitem.h"
|
|
|
|
#include <QMatrix4x4>
|
|
#include <QSGClipNode>
|
|
|
|
class LocalTransform : public QQuickTransform
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
LocalTransform(QObject *parent) : QQuickTransform(parent) {}
|
|
|
|
void setTransform(const QMatrix4x4 &t) {
|
|
transform = t;
|
|
update();
|
|
}
|
|
void applyTo(QMatrix4x4 *matrix) const override
|
|
{
|
|
*matrix *= transform;
|
|
}
|
|
private:
|
|
QMatrix4x4 transform;
|
|
};
|
|
|
|
CanvasItem::CanvasItem(QQuickItem* pr)
|
|
: QQuickItem(pr)
|
|
, m_localTransform(new LocalTransform(this))
|
|
{
|
|
setFlag(ItemHasContents);
|
|
m_localTransform->prependToItem(this);
|
|
}
|
|
|
|
void CanvasItem::setTransform(const QMatrix4x4 &mat)
|
|
{
|
|
m_localTransform->setTransform(mat);
|
|
}
|
|
|
|
void CanvasItem::setClip(const QRectF &clip, ReferenceFrame rf)
|
|
{
|
|
if (m_hasClip && (clip == m_clipRect) && (rf == m_clipReferenceFrame)) {
|
|
return;
|
|
}
|
|
|
|
m_hasClip = true;
|
|
m_clipRect = clip;
|
|
m_clipReferenceFrame = rf;
|
|
update();
|
|
}
|
|
|
|
void CanvasItem::setClipReferenceFrameItem(QQuickItem *refItem)
|
|
{
|
|
m_clipReferenceFrameItem = refItem;
|
|
}
|
|
|
|
void CanvasItem::clearClip()
|
|
{
|
|
m_hasClip = false;
|
|
update();
|
|
}
|
|
|
|
QSGNode *CanvasItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *d)
|
|
{
|
|
QSGNode* realOldNode = oldNode;
|
|
QSGClipNode* oldClip = nullptr;
|
|
|
|
if (oldNode && (oldNode->type() == QSGNode::ClipNodeType)) {
|
|
Q_ASSERT(oldNode->childCount() == 1);
|
|
realOldNode = oldNode->childAtIndex(0);
|
|
oldClip = static_cast<QSGClipNode*>(oldNode);
|
|
}
|
|
|
|
QSGNode* contentNode = updateRealPaintNode(realOldNode, d);
|
|
if (!contentNode) {
|
|
return nullptr;
|
|
}
|
|
|
|
QSGNode* clipNode = updateClipNode(oldClip, contentNode);
|
|
return clipNode ? clipNode : contentNode;
|
|
}
|
|
|
|
QSGNode *CanvasItem::updateRealPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *d)
|
|
{
|
|
if (oldNode) {
|
|
return oldNode;
|
|
}
|
|
|
|
return new QSGNode();
|
|
}
|
|
|
|
QRectF checkRectangularClip(QPointF* vertices)
|
|
{
|
|
// order is TL / BL / TR / BR to match updateRectGeometry
|
|
const double top = vertices[0].y();
|
|
const double left = vertices[0].x();
|
|
const double bottom = vertices[1].y();
|
|
const double right = vertices[2].x();
|
|
|
|
if (vertices[1].x() != left) return {};
|
|
if (vertices[2].y() != top) return {};
|
|
if ((vertices[3].x() != right) || (vertices[3].y() != bottom))
|
|
return {};
|
|
|
|
return QRectF(vertices[0], vertices[3]);
|
|
}
|
|
|
|
QSGClipNode* CanvasItem::updateClipNode(QSGClipNode* oldClipNode, QSGNode* contentNode)
|
|
{
|
|
Q_ASSERT(contentNode);
|
|
if (!m_hasClip) {
|
|
return nullptr;
|
|
}
|
|
|
|
QSGGeometry* clipGeometry = nullptr;
|
|
QSGClipNode* clipNode = oldClipNode;
|
|
|
|
if (!clipNode) {
|
|
clipNode = new QSGClipNode();
|
|
clipGeometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
|
|
clipGeometry->setDrawingMode(GL_TRIANGLE_STRIP);
|
|
clipNode->setGeometry(clipGeometry);
|
|
clipNode->setFlag(QSGNode::OwnsGeometry);
|
|
clipNode->appendChildNode(contentNode);
|
|
} else {
|
|
if (clipNode->childCount() == 1) {
|
|
const auto existingChild = clipNode->childAtIndex(0);
|
|
if (existingChild == contentNode) {
|
|
qInfo() << "optimise for this case!";
|
|
}
|
|
}
|
|
|
|
clipNode->removeAllChildNodes();
|
|
clipNode->appendChildNode(contentNode);
|
|
clipGeometry = clipNode->geometry();
|
|
Q_ASSERT(clipGeometry);
|
|
}
|
|
|
|
QPointF clipVertices[4],
|
|
inVertices[4] = {m_clipRect.topLeft(), m_clipRect.bottomLeft(),
|
|
m_clipRect.topRight(), m_clipRect.bottomRight()};
|
|
QRectF rectClip;
|
|
|
|
switch (m_clipReferenceFrame) {
|
|
case ReferenceFrame::GLOBAL:
|
|
case ReferenceFrame::PARENT:
|
|
Q_ASSERT(m_clipReferenceFrameItem);
|
|
for (int i=0; i<4; ++i) {
|
|
clipVertices[i] = mapFromItem(m_clipReferenceFrameItem, inVertices[i]);
|
|
}
|
|
rectClip = checkRectangularClip(clipVertices);
|
|
break;
|
|
|
|
case ReferenceFrame::LOCAL:
|
|
// local ref-frame clip is always rectangular
|
|
rectClip = m_clipRect;
|
|
for (int i=0; i<4; ++i) {
|
|
clipVertices[i] = inVertices[i];
|
|
}
|
|
break;
|
|
}
|
|
|
|
clipNode->setIsRectangular(!rectClip.isNull());
|
|
qInfo() << "\nobj:" << objectName();
|
|
if (!rectClip.isNull()) {
|
|
qInfo() << "have rectangular clip for:" << m_clipRect << (int) m_clipReferenceFrame << rectClip;
|
|
clipNode->setClipRect(rectClip);
|
|
} else {
|
|
qInfo() << "haved rotated clip" << m_clipRect << (int) m_clipReferenceFrame;
|
|
qInfo() << "final local clip points:" << clipVertices[0] << clipVertices[1]
|
|
<< clipVertices[2] << clipVertices[3];
|
|
}
|
|
|
|
QSGGeometry::Point2D *v = clipGeometry->vertexDataAsPoint2D();
|
|
for (int i=0; i<4; ++i) {
|
|
v[i].x = clipVertices[i].x();
|
|
v[i].y = clipVertices[i].y();
|
|
}
|
|
clipGeometry->markVertexDataDirty();
|
|
clipNode->markDirty(QSGNode::DirtyGeometry);
|
|
return clipNode;
|
|
}
|
|
|
|
#include "canvasitem.moc"
|