199 lines
5.7 KiB
C++
199 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"
|