KDEGames

kgamerenderedgraphicsobject.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Stefan Majewsky <majewsky@gmx.net>
3
4 SPDX-License-Identifier: LGPL-2.0-only
5*/
6
7#include "kgamerenderedgraphicsobject.h"
8
9// own
10#include "kgamegraphicsviewrenderer.h"
11// Qt
12#include <QGraphicsView>
13#include <QtMath>
14
15class KGameRenderedGraphicsObjectPrivate : public QGraphicsPixmapItem
16{
17public:
18 explicit KGameRenderedGraphicsObjectPrivate(KGameRenderedGraphicsObject *parent);
19
20 bool adjustRenderSize(); // returns whether an adjustment was made; WARNING: only call when m_primaryView != 0
21 void adjustTransform();
22
23 // QGraphicsItem reimplementations (see comment below for why we need all of this)
24 bool contains(const QPointF &point) const override;
25 bool isObscuredBy(const QGraphicsItem *item) const override;
26 QPainterPath opaqueArea() const override;
27 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
28 QPainterPath shape() const override;
29
30public:
31 KGameRenderedGraphicsObject *const m_parent;
32
33 QGraphicsView *m_primaryView = nullptr;
34 QSize m_correctRenderSize = {0, 0};
35 QSizeF m_fixedSize = {-1, -1};
36};
37
38KGameRenderedGraphicsObjectPrivate::KGameRenderedGraphicsObjectPrivate(KGameRenderedGraphicsObject *parent)
39 : QGraphicsPixmapItem(parent)
40 , m_parent(parent)
41{
42}
43
44static inline int vectorLength(QPointF point)
45{
46 return qSqrt(point.x() * point.x() + point.y() * point.y());
47}
48
49bool KGameRenderedGraphicsObjectPrivate::adjustRenderSize()
50{
51 Q_ASSERT(m_primaryView);
52 // create a polygon from the item's boundingRect
53 const QRectF itemRect = m_parent->boundingRect();
54 QPolygonF itemPolygon(3);
55 itemPolygon[0] = itemRect.topLeft();
56 itemPolygon[1] = itemRect.topRight();
57 itemPolygon[2] = itemRect.bottomLeft();
58 // determine correct render size
59 const QPolygonF scenePolygon = m_parent->sceneTransform().map(itemPolygon);
60 const QPolygon viewPolygon = m_primaryView->mapFromScene(scenePolygon);
61 m_correctRenderSize.setWidth(qMax(vectorLength(viewPolygon[1] - viewPolygon[0]), 1));
62 m_correctRenderSize.setHeight(qMax(vectorLength(viewPolygon[2] - viewPolygon[0]), 1));
63 // ignore fluctuations in the render size which result from rounding errors
64 const QSize diff = m_parent->renderSize() - m_correctRenderSize;
65 if (qAbs(diff.width()) <= 1 && qAbs(diff.height()) <= 1) {
66 return false;
67 }
68 m_parent->setRenderSize(m_correctRenderSize);
69 adjustTransform();
70 return true;
71}
72
73void KGameRenderedGraphicsObjectPrivate::adjustTransform()
74{
75 // calculate new transform for this item
76 QTransform t;
77 t.scale(m_fixedSize.width() / m_correctRenderSize.width(), m_fixedSize.height() / m_correctRenderSize.height());
78 // render item
79 m_parent->prepareGeometryChange();
80 setTransform(t);
81 m_parent->update();
82}
83
84KGameRenderedGraphicsObject::KGameRenderedGraphicsObject(KGameGraphicsViewRenderer *renderer, const QString &spriteKey, QGraphicsItem *parent)
85 : QGraphicsObject(parent)
86 , KGameRendererClient(renderer, spriteKey)
87 , d_ptr(new KGameRenderedGraphicsObjectPrivate(this))
88{
89 setPrimaryView(renderer->defaultPrimaryView());
90}
91
92KGameRenderedGraphicsObject::~KGameRenderedGraphicsObject() = default;
93
95{
97
98 return d->pos();
99}
100
102{
104
105 if (d->pos() != offset) {
107 d->setPos(offset);
108 update();
109 }
110}
111
113{
114 setOffset(QPointF(x, y));
115}
116
118{
120
121 return d->m_fixedSize;
122}
123
125{
127
128 if (d->m_primaryView) {
129 d->m_fixedSize = fixedSize.expandedTo(QSize(1, 1));
130 d->adjustTransform();
131 }
132}
133
135{
137
138 return d->m_primaryView;
139}
140
142{
144
145 if (d->m_primaryView != view) {
146 d->m_primaryView = view;
147 if (view) {
148 if (!d->m_fixedSize.isValid()) {
149 d->m_fixedSize = QSize(1, 1);
150 }
151 // determine render size and adjust coordinate system
152 d->m_correctRenderSize = QSize(-10, -10); // force adjustment to be made
153 d->adjustRenderSize();
154 } else {
155 d->m_fixedSize = QSize(-1, -1);
156 // reset transform to make coordinate systems of this item and the private item equal
158 d->setTransform(QTransform());
159 update();
160 }
161 }
162}
163
165{
167
169 d->setPixmap(pixmap);
170 update();
171}
172
173// We want to make sure that all interactional events are sent ot this item, and
174// not to the contained QGraphicsPixmapItem which provides the visual
175// representation (and the metrics calculations).
176// At the same time, we do not want the contained QGraphicsPixmapItem to slow
177// down operations like QGraphicsScene::collidingItems().
178// So the strategy is to use the QGraphicsPixmapItem implementation from
179// KGameRenderedGraphicsObjectPrivate for KGameRenderedGraphicsObject.
180// Then the relevant methods in KGameRenderedGraphicsObjectPrivate are reimplemented empty
181// to effectively clear the item and hide it from any collision detection. This
182// strategy allows us to use the nifty QGraphicsPixmapItem logic without exposing
183// a QGraphicsPixmapItem subclass (which would conflict with QGraphicsObject).
184
185// BEGIN QGraphicsItem reimplementation of KGameRenderedGraphicsObject
186
187QRectF KGameRenderedGraphicsObject::boundingRect() const
188{
190
191 return d->mapRectToParent(d->QGraphicsPixmapItem::boundingRect());
192}
193
194bool KGameRenderedGraphicsObject::contains(const QPointF &point) const
195{
197
198 return d->QGraphicsPixmapItem::contains(d->mapFromParent(point));
199}
200
201bool KGameRenderedGraphicsObject::isObscuredBy(const QGraphicsItem *item) const
202{
204
205 return d->QGraphicsPixmapItem::isObscuredBy(item);
206}
207
208QPainterPath KGameRenderedGraphicsObject::opaqueArea() const
209{
211
212 return d->mapToParent(d->QGraphicsPixmapItem::opaqueArea());
213}
214
215void KGameRenderedGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
216{
217 Q_UNUSED(painter)
218 Q_UNUSED(option)
219 Q_UNUSED(widget)
220}
221
222QPainterPath KGameRenderedGraphicsObject::shape() const
223{
225
226 return d->mapToParent(d->QGraphicsPixmapItem::shape());
227}
228
229// END QGraphicsItem reimplementation of KGameRenderedGraphicsObject
230// BEGIN QGraphicsItem reimplementation of KGameRenderedGraphicsObjectPrivate
231
232bool KGameRenderedGraphicsObjectPrivate::contains(const QPointF &point) const
233{
234 Q_UNUSED(point)
235 return false;
236}
237
238bool KGameRenderedGraphicsObjectPrivate::isObscuredBy(const QGraphicsItem *item) const
239{
240 Q_UNUSED(item)
241 return false;
242}
243
244QPainterPath KGameRenderedGraphicsObjectPrivate::opaqueArea() const
245{
246 return QPainterPath();
247}
248
249void KGameRenderedGraphicsObjectPrivate::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
250{
251 // Trivial stuff up to now. The fun stuff starts here. ;-)
252 // There is no way to get informed when the viewport's coordinate system
253 //(relative to this item's coordinate system) has changed, so we're checking
254 // the renderSize in each paintEvent coming from the primary view.
255 if (m_primaryView) {
256 if (m_primaryView == widget || m_primaryView->isAncestorOf(widget)) {
257 const bool isSimpleTransformation = !painter->transform().isRotating();
258 // If an adjustment was made, do not paint now, but wait for the next
259 // painting. However, paint directly if the transformation is
260 // complex, in order to avoid flicker.
261 if (adjustRenderSize()) {
262 if (isSimpleTransformation) {
263 return;
264 }
265 }
266 if (isSimpleTransformation) {
267 // draw pixmap directly in physical coordinates
268 const QPoint basePos = painter->transform().map(QPointF()).toPoint();
269 painter->save();
270 painter->setTransform(QTransform());
271 painter->drawPixmap(basePos, pixmap());
272 painter->restore();
273 return;
274 }
275 }
276 }
277 QGraphicsPixmapItem::paint(painter, option, widget);
278}
279
280QPainterPath KGameRenderedGraphicsObjectPrivate::shape() const
281{
282 return QPainterPath();
283}
284
285// END QGraphicsItem reimplementation of KGameRenderedGraphicsObjectPrivate
286
287#include "moc_kgamerenderedgraphicsobject.cpp"
A QGraphicsObject which displays pixmaps from a KGameRenderer.
void setFixedSize(QSizeF size)
Sets the fixed size of this item, i.e.
void setPrimaryView(QGraphicsView *view)
Sets the primary view of this item.
void setOffset(QPointF offset)
Sets the item's offset, which defines the point of the top-left corner of the bounding rect,...
QGraphicsView * primaryView() const
Returns a pointer to the current primary view, or 0 if no primary view has been set (which is the def...
KGameRenderedGraphicsObject(KGameGraphicsViewRenderer *renderer, const QString &spriteKey, QGraphicsItem *parent=nullptr)
Creates a new KGameRenderedGraphicsObject which renders the sprite with the given spriteKey as provid...
void receivePixmap(const QPixmap &pixmap) override
This method is called when the KGameRenderer has provided a new pixmap for this client (esp.
An object that receives pixmaps from a KGameRenderer.
KGameRenderer * renderer() const
void setRenderSize(QSize renderSize)
Defines the size of the pixmap that will be requested from KGameRenderer.
void prepareGeometryChange()
QTransform sceneTransform() const const
void setTransform(const QTransform &matrix, bool combine)
void update(const QRectF &rect)
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
QPixmap pixmap() const const
QPainterPath mapFromScene(const QPainterPath &path) const const
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
void restore()
void save()
void setTransform(const QTransform &transform, bool combine)
const QTransform & transform() const const
qreal x() const const
qreal y() const const
QPointF bottomLeft() const const
QPointF topLeft() const const
QPointF topRight() const const
int height() const const
void setHeight(int height)
void setWidth(int width)
int width() const const
QSizeF expandedTo(const QSizeF &otherSize) const const
qreal height() const const
qreal width() const const
bool isRotating() const const
QLine map(const QLine &l) const const
QTransform & scale(qreal sx, qreal sy)
bool isAncestorOf(const QWidget *child) const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:59:04 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.