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));
59QSGAreaAllocator::QSGAreaAllocator(
const QSize &size) : m_size(size)
61 m_root =
new QSGAreaAllocatorNode(
nullptr);
64QSGAreaAllocator::~QSGAreaAllocator()
69QRect QSGAreaAllocator::allocate(
const QSize &size)
72 bool result = allocateInNode(size, point, QRect(QPoint(0, 0), m_size), m_root);
73 return result ? QRect(point, size) : QRect();
76bool QSGAreaAllocator::deallocate(
const QRect &rect)
78 return deallocateInNode(rect.topLeft(), m_root);
81bool QSGAreaAllocator::allocateInNode(
const QSize &size, QPoint &result,
const QRect ¤tRect, QSGAreaAllocatorNode *node)
83 if (size.width() > currentRect.width() || size.height() > currentRect.height())
89 if (size.width() + maxMargin >= currentRect.width() && size.height() + maxMargin >= currentRect.height()) {
91 node->isOccupied =
true;
92 result = currentRect.topLeft();
97 node->left =
new QSGAreaAllocatorNode(node);
98 node->right =
new QSGAreaAllocatorNode(node);
99 QRect splitRect = currentRect;
100 if ((currentRect.width() - size.width()) * currentRect.height() < (currentRect.height() - size.height()) * currentRect.width()) {
101 node->splitType = HorizontalSplit;
102 node->split = currentRect.top() + size.height();
103 splitRect.setHeight(size.height());
105 node->splitType = VerticalSplit;
106 node->split = currentRect.left() + size.width();
107 splitRect.setWidth(size.width());
109 return allocateInNode(size, result, splitRect, node->left);
113 QRect leftRect = currentRect;
114 QRect rightRect = currentRect;
115 if (node->splitType == HorizontalSplit) {
116 leftRect.setHeight(node->split - leftRect.top());
117 rightRect.setTop(node->split);
119 leftRect.setWidth(node->split - leftRect.left());
120 rightRect.setLeft(node->split);
122 if (allocateInNode(size, result, leftRect, node->left))
124 if (allocateInNode(size, result, rightRect, node->right))
130bool QSGAreaAllocator::deallocateInNode(
const QPoint &pos, QSGAreaAllocatorNode *node)
132 while (!node->isLeaf()) {
134 int cmp = node->splitType == HorizontalSplit ? pos.y() : pos.x();
135 node = cmp < node->split ? node->left : node->right;
137 if (!node->isOccupied)
139 node->isOccupied =
false;
140 mergeNodeWithNeighbors(node);
144void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node)
147 QSGAreaAllocatorNode *parent =
nullptr;
148 QSGAreaAllocatorNode *current =
nullptr;
149 QSGAreaAllocatorNode *sibling;
151 Q_ASSERT(node->isLeaf());
152 Q_ASSERT(!node->isOccupied);
153 if (node->parent ==
nullptr)
156 SplitType splitType = SplitType(node->parent->splitType);
160
161
162
163
164
165
166
167
168
169
170
171
172
173
177 parent = current->parent;
178 while (parent && current == parent->left && parent->splitType == splitType) {
180 parent = parent->parent;
183 if (parent && parent->splitType == splitType) {
184 Q_ASSERT(current == parent->right);
185 Q_ASSERT(parent->left);
187 QSGAreaAllocatorNode *neighbor = parent->left;
188 while (neighbor->right && neighbor->splitType == splitType)
189 neighbor = neighbor->right;
191 if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) {
193 parent->split = neighbor->parent->split;
195 parent = neighbor->parent;
196 sibling = neighbor == parent->left ? parent->right : parent->left;
197 QSGAreaAllocatorNode **nodeRef = &m_root;
198 if (parent->parent) {
199 if (parent == parent->parent->left)
200 nodeRef = &parent->parent->left;
202 nodeRef = &parent->parent->right;
204 sibling->parent = parent->parent;
206 parent->left = parent->right =
nullptr;
215 parent = current->parent;
216 while (parent && current == parent->right && parent->splitType == splitType) {
218 parent = parent->parent;
221 if (parent && parent->splitType == splitType) {
222 Q_ASSERT(current == parent->left);
223 Q_ASSERT(parent->right);
225 QSGAreaAllocatorNode *neighbor = parent->right;
226 while (neighbor->left && neighbor->splitType == splitType)
227 neighbor = neighbor->left;
229 if (neighbor->isLeaf() && neighbor->parent->splitType == splitType && !neighbor->isOccupied) {
231 parent->split = neighbor->parent->split;
233 parent = neighbor->parent;
234 sibling = neighbor == parent->left ? parent->right : parent->left;
235 QSGAreaAllocatorNode **nodeRef = &m_root;
236 if (parent->parent) {
237 if (parent == parent->parent->left)
238 nodeRef = &parent->parent->left;
240 nodeRef = &parent->parent->right;
242 sibling->parent = parent->parent;
244 parent->left = parent->right =
nullptr;
254 struct AreaAllocatorTable
280 template <
typename T>
281 static inline T fetch(
const char *data, Offset offset)
283 return qFromBigEndian<T>(data +
int(offset));
286 template <
typename T>
287 static inline void put(
char *data, Offset offset, T value)
289 qToBigEndian(value, data +
int(offset));
294QByteArray QSGAreaAllocator::serialize()
296 QVarLengthArray<QSGAreaAllocatorNode *> nodesToProcess;
298 QStack<QSGAreaAllocatorNode *> nodes;
300 while (!nodes.isEmpty()) {
301 QSGAreaAllocatorNode *node = nodes.pop();
303 nodesToProcess.append(node);
304 if (node->left !=
nullptr)
305 nodes.push(node->left);
306 if (node->right !=
nullptr)
307 nodes.push(node->right);
311 ret.resize(AreaAllocatorTable::HeaderSize + AreaAllocatorTable::NodeSize * nodesToProcess.size());
313 char *data = ret.data();
314 AreaAllocatorTable::put(data, AreaAllocatorTable::majorVersion, quint8(5));
315 AreaAllocatorTable::put(data, AreaAllocatorTable::minorVersion, quint8(12));
316 AreaAllocatorTable::put(data, AreaAllocatorTable::width, quint32(m_size.width()));
317 AreaAllocatorTable::put(data, AreaAllocatorTable::height, quint32(m_size.height()));
319 data += AreaAllocatorTable::HeaderSize;
320 for (QSGAreaAllocatorNode *node : nodesToProcess) {
321 AreaAllocatorTable::put(data, AreaAllocatorTable::split, qint32(node->split));
322 AreaAllocatorTable::put(data, AreaAllocatorTable::splitType, quint32(node->splitType));
325 (node->isOccupied ? AreaAllocatorTable::IsOccupied : 0)
326 | (node->left !=
nullptr ? AreaAllocatorTable::HasLeft : 0)
327 | (node->right !=
nullptr ? AreaAllocatorTable::HasRight : 0);
328 AreaAllocatorTable::put(data, AreaAllocatorTable::flags, flags);
329 data += AreaAllocatorTable::NodeSize;
335const char *QSGAreaAllocator::deserialize(
const char *data,
int size)
337 if (uint(size) < AreaAllocatorTable::HeaderSize) {
338 qWarning(
"QSGAreaAllocator::deserialize: Data not long enough to fit header");
342 const char *end = data + size;
344 quint8 majorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::majorVersion);
345 quint8 minorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::minorVersion);
346 if (majorVersion != 5 || minorVersion != 12) {
347 qWarning(
"Unrecognized version %d.%d of QSGAreaAllocator",
353 m_size = QSize(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::width),
354 AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::height));
356 Q_ASSERT(m_root !=
nullptr);
357 Q_ASSERT(m_root->left ==
nullptr);
358 Q_ASSERT(m_root->right ==
nullptr);
360 QStack<QSGAreaAllocatorNode *> nodes;
363 data += AreaAllocatorTable::HeaderSize;
364 while (!nodes.isEmpty()) {
365 if (data + AreaAllocatorTable::NodeSize > end) {
366 qWarning(
"QSGAreaAllocator::deseriable: Data not long enough for nodes");
370 QSGAreaAllocatorNode *node = nodes.pop();
372 node->split = AreaAllocatorTable::fetch<qint32>(data, AreaAllocatorTable::split);
373 node->splitType = SplitType(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::splitType));
375 quint8 flags = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::flags);
376 node->isOccupied = flags & AreaAllocatorTable::IsOccupied;
378 if (flags & AreaAllocatorTable::HasLeft) {
379 node->left =
new QSGAreaAllocatorNode(node);
380 nodes.push(node->left);
383 if (flags & AreaAllocatorTable::HasRight) {
384 node->right =
new QSGAreaAllocatorNode(node);
385 nodes.push(node->right);
388 data += AreaAllocatorTable::NodeSize;
Combined button and popup list for selecting options.
static const int maxMargin
QSGAreaAllocatorNode(QSGAreaAllocatorNode *parent)
QSGAreaAllocatorNode * parent
QSGAreaAllocatorNode * left
QSGAreaAllocatorNode * right