6#include <QtCore/qglobal.h>
7#include <QtCore/qrect.h>
8#include <QtCore/qpoint.h>
9#include <QtCore/qdatastream.h>
10#include <QtCore/qstack.h>
11#include <QtCore/qendian.h>
12#include <QtCore/qvarlengtharray.h>
54 Q_ASSERT((
left !=
nullptr) == (
right !=
nullptr));
61QSGAreaAllocator::QSGAreaAllocator(
const QSize &size,
int sliceHeight)
63 , m_sliceHeight(sliceHeight)
65 m_root =
new QSGAreaAllocatorNode(
nullptr);
68QSGAreaAllocator::~QSGAreaAllocator()
73QRect QSGAreaAllocator::allocate(
const QSize &size)
76 bool result = allocateInNode(size, point, QRect(QPoint(0, 0), m_size), m_root);
77 return result ? QRect(point, size) : QRect();
80bool QSGAreaAllocator::deallocate(
const QRect &rect)
82 return deallocateInNode(rect.topLeft(), m_root);
85bool QSGAreaAllocator::allocateInNode(
const QSize &size, QPoint &result,
const QRect ¤tRect, QSGAreaAllocatorNode *node)
87 if (size.width() > currentRect.width() || size.height() > currentRect.height())
93 if (m_sliceHeight > 0) {
96 const int boundary = (currentRect.top() / m_sliceHeight + 1) * m_sliceHeight;
97 if (boundary <= currentRect.bottom()) {
98 node->left =
new QSGAreaAllocatorNode(node);
99 node->right =
new QSGAreaAllocatorNode(node);
100 node->splitType = HorizontalSplit;
101 node->split = boundary;
105 return allocateInNode(size, result, currentRect, node);
108 if (size.width() + maxMargin >= currentRect.width() && size.height() + maxMargin >= currentRect.height()) {
110 node->isOccupied =
true;
111 result = currentRect.topLeft();
116 node->left =
new QSGAreaAllocatorNode(node);
117 node->right =
new QSGAreaAllocatorNode(node);
118 QRect splitRect = currentRect;
119 if ((currentRect.width() - size.width()) * currentRect.height() < (currentRect.height() - size.height()) * currentRect.width()) {
120 node->splitType = HorizontalSplit;
121 node->split = currentRect.top() + size.height();
122 splitRect.setHeight(size.height());
124 node->splitType = VerticalSplit;
125 node->split = currentRect.left() + size.width();
126 splitRect.setWidth(size.width());
128 return allocateInNode(size, result, splitRect, node->left);
132 QRect leftRect = currentRect;
133 QRect rightRect = currentRect;
134 if (node->splitType == HorizontalSplit) {
135 leftRect.setHeight(node->split - leftRect.top());
136 rightRect.setTop(node->split);
138 leftRect.setWidth(node->split - leftRect.left());
139 rightRect.setLeft(node->split);
141 if (allocateInNode(size, result, leftRect, node->left))
143 if (allocateInNode(size, result, rightRect, node->right))
149bool QSGAreaAllocator::deallocateInNode(
const QPoint &pos, QSGAreaAllocatorNode *node)
151 while (!node->isLeaf()) {
153 int cmp = node->splitType == HorizontalSplit ? pos.y() : pos.x();
154 node = cmp < node->split ? node->left : node->right;
156 if (!node->isOccupied)
158 node->isOccupied =
false;
159 mergeNodeWithNeighbors(node);
163void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node)
166 QSGAreaAllocatorNode *parent =
nullptr;
167 QSGAreaAllocatorNode *current =
nullptr;
168 QSGAreaAllocatorNode *sibling;
170 Q_ASSERT(node->isLeaf());
171 Q_ASSERT(!node->isOccupied);
172 if (node->parent ==
nullptr)
175 SplitType splitType = SplitType(node->parent->splitType);
179
180
181
182
183
184
185
186
187
188
189
190
191
192
196 parent = current->parent;
197 while (parent && current == parent->left && parent->splitType == splitType) {
199 parent = parent->parent;
202 if (parent && parent->splitType == splitType) {
203 Q_ASSERT(current == parent->right);
204 Q_ASSERT(parent->left);
206 QSGAreaAllocatorNode *neighbor = parent->left;
207 while (neighbor->right && neighbor->splitType == splitType)
208 neighbor = neighbor->right;
210 if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) {
212 parent->split = neighbor->parent->split;
214 parent = neighbor->parent;
215 sibling = neighbor == parent->left ? parent->right : parent->left;
216 QSGAreaAllocatorNode **nodeRef = &m_root;
217 if (parent->parent) {
218 if (parent == parent->parent->left)
219 nodeRef = &parent->parent->left;
221 nodeRef = &parent->parent->right;
223 sibling->parent = parent->parent;
225 parent->left = parent->right =
nullptr;
234 parent = current->parent;
235 while (parent && current == parent->right && parent->splitType == splitType) {
237 parent = parent->parent;
240 if (parent && parent->splitType == splitType) {
241 Q_ASSERT(current == parent->left);
242 Q_ASSERT(parent->right);
244 QSGAreaAllocatorNode *neighbor = parent->right;
245 while (neighbor->left && neighbor->splitType == splitType)
246 neighbor = neighbor->left;
248 if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) {
250 parent->split = neighbor->parent->split;
252 parent = neighbor->parent;
253 sibling = neighbor == parent->left ? parent->right : parent->left;
254 QSGAreaAllocatorNode **nodeRef = &m_root;
255 if (parent->parent) {
256 if (parent == parent->parent->left)
257 nodeRef = &parent->parent->left;
259 nodeRef = &parent->parent->right;
261 sibling->parent = parent->parent;
263 parent->left = parent->right =
nullptr;
273 struct AreaAllocatorTable
299 template <
typename T>
300 static inline T fetch(
const char *data, Offset offset)
302 return qFromBigEndian<T>(data +
int(offset));
305 template <
typename T>
306 static inline void put(
char *data, Offset offset, T value)
308 qToBigEndian(value, data +
int(offset));
313QByteArray QSGAreaAllocator::serialize()
315 QVarLengthArray<QSGAreaAllocatorNode *> nodesToProcess;
317 QStack<QSGAreaAllocatorNode *> nodes;
319 while (!nodes.isEmpty()) {
320 QSGAreaAllocatorNode *node = nodes.pop();
322 nodesToProcess.append(node);
323 if (node->left !=
nullptr)
324 nodes.push(node->left);
325 if (node->right !=
nullptr)
326 nodes.push(node->right);
330 ret.resize(AreaAllocatorTable::HeaderSize + AreaAllocatorTable::NodeSize * nodesToProcess.size());
332 char *data = ret.data();
333 AreaAllocatorTable::put(data, AreaAllocatorTable::majorVersion, quint8(5));
334 AreaAllocatorTable::put(data, AreaAllocatorTable::minorVersion, quint8(12));
335 AreaAllocatorTable::put(data, AreaAllocatorTable::width, quint32(m_size.width()));
336 AreaAllocatorTable::put(data, AreaAllocatorTable::height, quint32(m_size.height()));
338 data += AreaAllocatorTable::HeaderSize;
339 for (QSGAreaAllocatorNode *node : nodesToProcess) {
340 AreaAllocatorTable::put(data, AreaAllocatorTable::split, qint32(node->split));
341 AreaAllocatorTable::put(data, AreaAllocatorTable::splitType, quint32(node->splitType));
344 (node->isOccupied ? AreaAllocatorTable::IsOccupied : 0)
345 | (node->left !=
nullptr ? AreaAllocatorTable::HasLeft : 0)
346 | (node->right !=
nullptr ? AreaAllocatorTable::HasRight : 0);
347 AreaAllocatorTable::put(data, AreaAllocatorTable::flags, flags);
348 data += AreaAllocatorTable::NodeSize;
354const char *QSGAreaAllocator::deserialize(
const char *data,
int size)
356 if (uint(size) < AreaAllocatorTable::HeaderSize) {
357 qWarning(
"QSGAreaAllocator::deserialize: Data not long enough to fit header");
361 const char *end = data + size;
363 quint8 majorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::majorVersion);
364 quint8 minorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::minorVersion);
365 if (majorVersion != 5 || minorVersion != 12) {
366 qWarning(
"Unrecognized version %d.%d of QSGAreaAllocator",
372 m_size = QSize(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::width),
373 AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::height));
375 Q_ASSERT(m_root !=
nullptr);
376 Q_ASSERT(m_root->left ==
nullptr);
377 Q_ASSERT(m_root->right ==
nullptr);
379 QStack<QSGAreaAllocatorNode *> nodes;
382 data += AreaAllocatorTable::HeaderSize;
383 while (!nodes.isEmpty()) {
384 if (data + AreaAllocatorTable::NodeSize > end) {
385 qWarning(
"QSGAreaAllocator::deseriable: Data not long enough for nodes");
389 QSGAreaAllocatorNode *node = nodes.pop();
391 node->split = AreaAllocatorTable::fetch<qint32>(data, AreaAllocatorTable::split);
392 node->splitType = SplitType(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::splitType));
394 quint8 flags = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::flags);
395 node->isOccupied = flags & AreaAllocatorTable::IsOccupied;
397 if (flags & AreaAllocatorTable::HasLeft) {
398 node->left =
new QSGAreaAllocatorNode(node);
399 nodes.push(node->left);
402 if (flags & AreaAllocatorTable::HasRight) {
403 node->right =
new QSGAreaAllocatorNode(node);
404 nodes.push(node->right);
407 data += AreaAllocatorTable::NodeSize;
Combined button and popup list for selecting options.
static const int maxMargin
QSGAreaAllocatorNode(QSGAreaAllocatorNode *parent)
QSGAreaAllocatorNode * parent
QSGAreaAllocatorNode * left
QSGAreaAllocatorNode * right