KDEGames

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

KDE's Doxygen guidelines are available online.