scene: Add support for item transformations

This commit is contained in:
Vlad Zahorodnii 2023-12-09 13:18:06 +02:00
parent 9e70c2a21c
commit f6b605daf2
8 changed files with 71 additions and 48 deletions

View file

@ -208,7 +208,7 @@ void EffectWindow::addRepaintFull()
void EffectWindow::addLayerRepaint(const QRect &r)
{
d->m_windowItem->scheduleRepaint(d->m_windowItem->mapFromGlobal(r));
d->m_windowItem->scheduleRepaint(d->m_windowItem->mapFromScene(r));
}
const EffectWindowGroup *EffectWindow::group() const

View file

@ -82,6 +82,7 @@ void Item::setParentItem(Item *item)
Q_ASSERT(m_parentItem->m_scene == m_scene);
m_parentItem->addChild(this);
}
updateItemToSceneTransform();
updateEffectiveVisibility();
}
@ -93,7 +94,7 @@ void Item::addChild(Item *item)
markSortedChildItemsDirty();
updateBoundingRect();
scheduleRepaint(item->boundingRect().translated(item->position()));
scheduleRepaint(item->transform().mapRect(item->boundingRect()).translated(item->position()));
Q_EMIT childAdded(item);
}
@ -101,7 +102,7 @@ void Item::addChild(Item *item)
void Item::removeChild(Item *item)
{
Q_ASSERT(m_childItems.contains(item));
scheduleRepaint(item->boundingRect().translated(item->position()));
scheduleRepaint(item->transform().mapRect(item->boundingRect()).translated(item->position()));
m_childItems.removeOne(item);
markSortedChildItemsDirty();
@ -124,6 +125,7 @@ void Item::setPosition(const QPointF &point)
if (m_position != point) {
scheduleRepaint(boundingRect());
m_position = point;
updateItemToSceneTransform();
if (m_parentItem) {
m_parentItem->updateBoundingRect();
}
@ -163,7 +165,7 @@ void Item::updateBoundingRect()
{
QRectF boundingRect = rect();
for (Item *item : std::as_const(m_childItems)) {
boundingRect |= item->boundingRect().translated(item->position());
boundingRect |= item->transform().mapRect(item->boundingRect()).translated(item->position());
}
if (m_boundingRect != boundingRect) {
m_boundingRect = boundingRect;
@ -184,51 +186,63 @@ QRegion Item::opaque() const
return QRegion();
}
QPointF Item::rootPosition() const
{
QPointF ret = position();
Item *parent = parentItem();
while (parent) {
ret += parent->position();
parent = parent->parentItem();
}
return ret;
}
QMatrix4x4 Item::transform() const
QTransform Item::transform() const
{
return m_transform;
}
void Item::setTransform(const QMatrix4x4 &transform)
void Item::setTransform(const QTransform &transform)
{
if (m_transform == transform) {
return;
}
scheduleRepaint(boundingRect());
m_transform = transform;
updateItemToSceneTransform();
if (m_parentItem) {
m_parentItem->updateBoundingRect();
}
scheduleRepaint(boundingRect());
}
QRegion Item::mapToGlobal(const QRegion &region) const
void Item::updateItemToSceneTransform()
{
m_itemToSceneTransform = m_transform;
if (!m_position.isNull()) {
m_itemToSceneTransform *= QTransform::fromTranslate(m_position.x(), m_position.y());
}
if (m_parentItem) {
m_itemToSceneTransform *= m_parentItem->m_itemToSceneTransform;
}
m_sceneToItemTransform = m_itemToSceneTransform.inverted();
for (Item *childItem : std::as_const(m_childItems)) {
childItem->updateItemToSceneTransform();
}
}
QRegion Item::mapToScene(const QRegion &region) const
{
if (region.isEmpty()) {
return QRegion();
}
return region.translated(rootPosition().toPoint());
return m_itemToSceneTransform.map(region);
}
QRectF Item::mapToGlobal(const QRectF &rect) const
QRectF Item::mapToScene(const QRectF &rect) const
{
if (rect.isEmpty()) {
return QRect();
}
return rect.translated(rootPosition());
return m_itemToSceneTransform.mapRect(rect);
}
QRectF Item::mapFromGlobal(const QRectF &rect) const
QRectF Item::mapFromScene(const QRectF &rect) const
{
if (rect.isEmpty()) {
return QRect();
}
return rect.translated(-rootPosition());
return m_sceneToItemTransform.mapRect(rect);
}
void Item::stackBefore(Item *sibling)
@ -303,7 +317,7 @@ void Item::scheduleRepaint(SceneDelegate *delegate, const QRegion &region)
void Item::scheduleRepaintInternal(const QRegion &region)
{
const QRegion globalRegion = mapToGlobal(region);
const QRegion globalRegion = mapToScene(region);
const QList<SceneDelegate *> delegates = m_scene->delegates();
for (SceneDelegate *delegate : delegates) {
const QRegion dirtyRegion = globalRegion & delegate->viewport();
@ -316,7 +330,7 @@ void Item::scheduleRepaintInternal(const QRegion &region)
void Item::scheduleRepaintInternal(SceneDelegate *delegate, const QRegion &region)
{
const QRegion globalRegion = mapToGlobal(region);
const QRegion globalRegion = mapToScene(region);
const QRegion dirtyRegion = globalRegion & delegate->viewport();
if (!dirtyRegion.isEmpty()) {
m_repaints[delegate] += dirtyRegion;
@ -329,7 +343,7 @@ void Item::scheduleFrame()
if (!isVisible()) {
return;
}
const QRect geometry = mapToGlobal(rect()).toRect();
const QRect geometry = mapToScene(rect()).toRect();
const QList<SceneDelegate *> delegates = m_scene->delegates();
for (SceneDelegate *delegate : delegates) {
if (delegate->viewport().intersects(geometry)) {
@ -412,7 +426,7 @@ void Item::updateEffectiveVisibility()
m_effectiveVisible = effectiveVisible;
if (!m_effectiveVisible) {
m_scene->addRepaint(mapToGlobal(boundingRect()).toAlignedRect());
m_scene->addRepaint(mapToScene(boundingRect()).toAlignedRect());
} else {
scheduleRepaintInternal(boundingRect().toAlignedRect());
}

View file

@ -11,9 +11,9 @@
#include "scene/itemgeometry.h"
#include <QList>
#include <QMatrix4x4>
#include <QObject>
#include <QPointer>
#include <QTransform>
#include <optional>
@ -69,26 +69,24 @@ public:
QList<Item *> childItems() const;
QList<Item *> sortedChildItems() const;
QPointF rootPosition() const;
QMatrix4x4 transform() const;
void setTransform(const QMatrix4x4 &transform);
QTransform transform() const;
void setTransform(const QTransform &transform);
/**
* Maps the given @a region from the item's coordinate system to the scene's coordinate
* system.
*/
QRegion mapToGlobal(const QRegion &region) const;
QRegion mapToScene(const QRegion &region) const;
/**
* Maps the given @a rect from the item's coordinate system to the scene's coordinate
* system.
*/
QRectF mapToGlobal(const QRectF &rect) const;
QRectF mapToScene(const QRectF &rect) const;
/**
* Maps the given @a rect from the scene's coordinate system to the item's coordinate
* system.
*/
QRectF mapFromGlobal(const QRectF &rect) const;
QRectF mapFromScene(const QRectF &rect) const;
/**
* Moves this item right before the specified @a sibling in the parent's children list.
@ -142,6 +140,7 @@ private:
void addChild(Item *item);
void removeChild(Item *item);
void updateBoundingRect();
void updateItemToSceneTransform();
void scheduleRepaintInternal(const QRegion &region);
void scheduleRepaintInternal(SceneDelegate *delegate, const QRegion &region);
void markSortedChildItemsDirty();
@ -153,7 +152,9 @@ private:
Scene *m_scene;
QPointer<Item> m_parentItem;
QList<Item *> m_childItems;
QMatrix4x4 m_transform;
QTransform m_transform;
QTransform m_itemToSceneTransform;
QTransform m_sceneToItemTransform;
QRectF m_boundingRect;
QPointF m_position;
QSizeF m_size = QSize(0, 0);

View file

@ -139,11 +139,19 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context)
{
const QList<Item *> sortedChildItems = item->sortedChildItems();
QMatrix4x4 matrix;
const auto logicalPosition = QVector2D(item->position().x(), item->position().y());
const auto scale = context->renderTargetScale;
QMatrix4x4 matrix;
matrix.translate(roundVector(logicalPosition * scale).toVector3D());
matrix *= item->transform();
if (context->transformStack.size() == 1) {
matrix *= context->rootTransform;
}
if (!item->transform().isIdentity()) {
matrix.scale(scale, scale);
matrix *= item->transform();
matrix.scale(1 / scale, 1 / scale);
}
context->transformStack.push(context->transformStack.top() * matrix);
context->opacityStack.push(context->opacityStack.top() * item->opacity());
@ -261,6 +269,7 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
RenderContext renderContext{
.projectionMatrix = viewport.projectionMatrix(),
.rootTransform = data.toMatrix(viewport.scale()), // TODO: unify transforms
.clip = region,
.hardwareClipping = region != infiniteRegion() && ((mask & Scene::PAINT_WINDOW_TRANSFORMED) || (mask & Scene::PAINT_SCREEN_TRANSFORMED)),
.renderTargetScale = viewport.scale(),
@ -269,8 +278,6 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
renderContext.transformStack.push(QMatrix4x4());
renderContext.opacityStack.push(data.opacity());
item->setTransform(data.toMatrix(renderContext.renderTargetScale));
createRenderNode(item, &renderContext);
int totalVertexCount = 0;

View file

@ -36,6 +36,7 @@ public:
QStack<QMatrix4x4> transformStack;
QStack<qreal> opacityStack;
const QMatrix4x4 projectionMatrix;
const QMatrix4x4 rootTransform;
const QRegion clip;
const bool hardwareClipping;
const qreal renderTargetScale;

View file

@ -61,7 +61,7 @@ void ItemRendererQPainter::renderItem(const RenderTarget &renderTarget, const Re
{
QRegion region = _region;
const QRect boundingRect = item->mapToGlobal(item->boundingRect()).toAlignedRect();
const QRect boundingRect = item->mapToScene(item->boundingRect()).toAlignedRect();
if (!(mask & (Scene::PAINT_WINDOW_TRANSFORMED | Scene::PAINT_SCREEN_TRANSFORMED))) {
region &= boundingRect;
}

View file

@ -320,12 +320,12 @@ void WorkspaceScene::preparePaintSimpleScreen()
if (window->opacity() == 1.0) {
const SurfaceItem *surfaceItem = windowItem->surfaceItem();
if (Q_LIKELY(surfaceItem)) {
data.opaque = surfaceItem->mapToGlobal(surfaceItem->opaque());
data.opaque = surfaceItem->mapToScene(surfaceItem->opaque());
}
const DecorationItem *decorationItem = windowItem->decorationItem();
if (decorationItem) {
data.opaque += decorationItem->mapToGlobal(decorationItem->opaque());
data.opaque += decorationItem->mapToScene(decorationItem->opaque());
}
}
@ -418,7 +418,7 @@ void WorkspaceScene::paintSimpleScreen(const RenderTarget &renderTarget, const R
data->region = visible;
if (!(data->mask & PAINT_WINDOW_TRANSFORMED)) {
data->region &= data->item->mapToGlobal(data->item->boundingRect()).toAlignedRect();
data->region &= data->item->mapToScene(data->item->boundingRect()).toAlignedRect();
if (!(data->mask & PAINT_WINDOW_TRANSLUCENT)) {
visible -= data->opaque;
@ -433,7 +433,7 @@ void WorkspaceScene::paintSimpleScreen(const RenderTarget &renderTarget, const R
}
if (m_dndIcon) {
const QRegion repaint = region & m_dndIcon->mapToGlobal(m_dndIcon->boundingRect()).toRect();
const QRegion repaint = region & m_dndIcon->mapToScene(m_dndIcon->boundingRect()).toRect();
if (!repaint.isEmpty()) {
m_renderer->renderItem(renderTarget, viewport, m_dndIcon.get(), 0, repaint, WindowPaintData{});
}

View file

@ -159,7 +159,7 @@ QDebug operator<<(QDebug debug, const Window *window)
QRectF Window::visibleGeometry() const
{
if (const WindowItem *item = windowItem()) {
return item->mapToGlobal(item->boundingRect());
return item->mapToScene(item->boundingRect());
}
return QRectF();
}