~ubuntu-branches/ubuntu/wily/libkdegames/wily-proposed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/***************************************************************************
 *   Copyright 2010 Stefan Majewsky <majewsky@gmx.net>                     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License          *
 *   version 2 as published by the Free Software Foundation                *
 *                                                                         *
 *   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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library 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 "kgamerenderedobjectitem.h"
#include "kgamerenderer.h"

#include <QtCore/qmath.h>
#include <QtWidgets/QGraphicsView>

class KGameRenderedObjectItemPrivate : public QGraphicsPixmapItem
{
	public:
		KGameRenderedObjectItemPrivate(KGameRenderedObjectItem* parent);
		bool adjustRenderSize(); //returns whether an adjustment was made; WARNING: only call when m_primaryView != 0
		void adjustTransform();

		//QGraphicsItem reimplementations (see comment below for why we need all of this)
		virtual bool contains(const QPointF& point) const;
		virtual bool isObscuredBy(const QGraphicsItem* item) const;
		virtual QPainterPath opaqueArea() const;
		virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
		virtual QPainterPath shape() const;
	public:
		KGameRenderedObjectItem* m_parent;
		QGraphicsView* m_primaryView;
		QSize m_correctRenderSize;
		QSizeF m_fixedSize;
};

KGameRenderedObjectItemPrivate::KGameRenderedObjectItemPrivate(KGameRenderedObjectItem* parent)
	: QGraphicsPixmapItem(parent)
	, m_parent(parent)
	, m_primaryView(0)
	, m_correctRenderSize(0, 0)
	, m_fixedSize(-1, -1)
{
}

static inline int vectorLength(const QPointF& point)
{
	return qSqrt(point.x() * point.x() + point.y() * point.y());
}

bool KGameRenderedObjectItemPrivate::adjustRenderSize()
{
	Q_ASSERT(m_primaryView);
	//create a polygon from the item's boundingRect
	const QRectF itemRect = m_parent->boundingRect();
	QPolygonF itemPolygon(3);
	itemPolygon[0] = itemRect.topLeft();
	itemPolygon[1] = itemRect.topRight();
	itemPolygon[2] = itemRect.bottomLeft();
	//determine correct render size
	const QPolygonF scenePolygon = m_parent->sceneTransform().map(itemPolygon);
	const QPolygon viewPolygon = m_primaryView->mapFromScene(scenePolygon);
	m_correctRenderSize.setWidth(qMax(vectorLength(viewPolygon[1] - viewPolygon[0]), 1));
	m_correctRenderSize.setHeight(qMax(vectorLength(viewPolygon[2] - viewPolygon[0]), 1));
	//ignore fluctuations in the render size which result from rounding errors
	const QSize diff = m_parent->renderSize() - m_correctRenderSize;
	if (qAbs(diff.width()) <= 1 && qAbs(diff.height()) <= 1)
	{
		return false;
	}
	m_parent->setRenderSize(m_correctRenderSize);
	adjustTransform();
	return true;
}

void KGameRenderedObjectItemPrivate::adjustTransform()
{
	//calculate new transform for this item
	QTransform t;
	t.scale(m_fixedSize.width() / m_correctRenderSize.width(), m_fixedSize.height() / m_correctRenderSize.height());
	//render item
	m_parent->prepareGeometryChange();
	setTransform(t);
	m_parent->update();
}

KGameRenderedObjectItem::KGameRenderedObjectItem(KGameRenderer* renderer, const QString& spriteKey, QGraphicsItem* parent)
	: QGraphicsObject(parent)
	, KGameRendererClient(renderer, spriteKey)
	, d(new KGameRenderedObjectItemPrivate(this))
{
	setPrimaryView(renderer->defaultPrimaryView());
}

KGameRenderedObjectItem::~KGameRenderedObjectItem()
{
	delete d;
}

QPointF KGameRenderedObjectItem::offset() const
{
	return d->pos();
}

void KGameRenderedObjectItem::setOffset(const QPointF& offset)
{
	if (d->pos() != offset)
	{
		prepareGeometryChange();
		d->setPos(offset);
		update();
	}
}

void KGameRenderedObjectItem::setOffset(qreal x, qreal y)
{
	setOffset(QPointF(x, y));
}

QSizeF KGameRenderedObjectItem::fixedSize() const
{
	return d->m_fixedSize;
}

void KGameRenderedObjectItem::setFixedSize(const QSizeF& fixedSize)
{
	if (d->m_primaryView)
	{
		d->m_fixedSize = fixedSize.expandedTo(QSize(1, 1));
		d->adjustTransform();
	}
}

QGraphicsView* KGameRenderedObjectItem::primaryView() const
{
	return d->m_primaryView;
}

void KGameRenderedObjectItem::setPrimaryView(QGraphicsView* view)
{
	if (d->m_primaryView != view)
	{
		d->m_primaryView = view;
		if (view)
		{
			if (!d->m_fixedSize.isValid())
			{
				d->m_fixedSize = QSize(1, 1);
			}
			//determine render size and adjust coordinate system
			d->m_correctRenderSize = QSize(-10, -10); //force adjustment to be made
			d->adjustRenderSize();
		}
		else
		{
			d->m_fixedSize = QSize(-1, -1);
			//reset transform to make coordinate systems of this item and the private item equal
			prepareGeometryChange();
			d->setTransform(QTransform());
			update();
		}
	}
}

void KGameRenderedObjectItem::receivePixmap(const QPixmap& pixmap)
{
	prepareGeometryChange();
	d->setPixmap(pixmap);
	update();
}

//We want to make sure that all interactional events are sent ot this item, and
//not to the contained QGraphicsPixmapItem which provides the visual
//representation (and the metrics calculations).
//At the same time, we do not want the contained QGraphicsPixmapItem to slow
//down operations like QGraphicsScene::collidingItems().
//So the strategy is to use the QGraphicsPixmapItem implementation from
//KGameRenderedObjectItemPrivate for KGameRenderedObjectItem.
//Then the relevant methods in KGameRenderedObjectItemPrivate are reimplemented empty
//to effectively clear the item and hide it from any collision detection. This
//strategy allows us to use the nifty QGraphicsPixmapItem logic without exposing
//a QGraphicsPixmapItem subclass (which would conflict with QGraphicsObject).

//BEGIN QGraphicsItem reimplementation of KGameRenderedObjectItem

QRectF KGameRenderedObjectItem::boundingRect() const
{
	return d->mapRectToParent(d->QGraphicsPixmapItem::boundingRect());
}

bool KGameRenderedObjectItem::contains(const QPointF& point) const
{
	return d->QGraphicsPixmapItem::contains(d->mapFromParent(point));
}

bool KGameRenderedObjectItem::isObscuredBy(const QGraphicsItem* item) const
{
	return d->QGraphicsPixmapItem::isObscuredBy(item);
}

QPainterPath KGameRenderedObjectItem::opaqueArea() const
{
	return d->mapToParent(d->QGraphicsPixmapItem::opaqueArea());
}

void KGameRenderedObjectItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	Q_UNUSED(painter) Q_UNUSED(option) Q_UNUSED(widget)
}

QPainterPath KGameRenderedObjectItem::shape() const
{
	return d->mapToParent(d->QGraphicsPixmapItem::shape());
}

//END QGraphicsItem reimplementation of KGameRenderedObjectItem
//BEGIN QGraphicsItem reimplementation of KGameRenderedObjectItemPrivate

bool KGameRenderedObjectItemPrivate::contains(const QPointF& point) const
{
	Q_UNUSED(point)
	return false;
}

bool KGameRenderedObjectItemPrivate::isObscuredBy(const QGraphicsItem* item) const
{
	Q_UNUSED(item)
	return false;
}

QPainterPath KGameRenderedObjectItemPrivate::opaqueArea() const
{
	return QPainterPath();
}

void KGameRenderedObjectItemPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	//Trivial stuff up to now. The fun stuff starts here. ;-)
	//There is no way to get informed when the viewport's coordinate system
	//(relative to this item's coordinate system) has changed, so we're checking
	//the renderSize in each paintEvent coming from the primary view.
	if (m_primaryView)
	{
		if (m_primaryView == widget || m_primaryView->isAncestorOf(widget))
		{
			const bool isSimpleTransformation = !painter->transform().isRotating();
			//If an adjustment was made, do not paint now, but wait for the next
			//painting. However, paint directly if the transformation is
			//complex, in order to avoid flicker.
			if (adjustRenderSize())
			{
				if (isSimpleTransformation)
				{
					return;
				}
			}
			if (isSimpleTransformation)
			{
				//draw pixmap directly in physical coordinates
				const QPoint basePos = painter->transform().map(QPointF()).toPoint();
				painter->save();
				painter->setTransform(QTransform());
				painter->drawPixmap(basePos, pixmap());
				painter->restore();
				return;
			}
		}
	}
	QGraphicsPixmapItem::paint(painter, option, widget);
}

QPainterPath KGameRenderedObjectItemPrivate::shape() const
{
	return QPainterPath();
}

//END QGraphicsItem reimplementation of KGameRenderedObjectItemPrivate