9#include <private/qsgadaptationlayer_p.h>
10#include <private/qsgdistancefieldglyphnode_p.h>
11#include <private/qquickclipnode_p.h>
12#include <private/qquickitem_p.h>
13#include <private/qquicktextdocument_p.h>
14#include <private/qsgrendernode_p.h>
16#include <QtCore/qpoint.h>
17#include <qtextdocument.h>
18#include <qtextlayout.h>
19#include <qabstracttextdocumentlayout.h>
20#include <private/qquickstyledtext_p.h>
21#include <private/qquicktext_p_p.h>
22#include <private/qfont_p.h>
23#include <private/qfontengine_p.h>
25#include <private/qtextdocumentlayout_p.h>
35
36
37
38
39
40
41QSGInternalTextNode::QSGInternalTextNode(QSGRenderContext *renderContext)
42 : m_renderContext(renderContext)
44#ifdef QSG_RUNTIME_DESCRIPTION
45 qsgnode_set_description(
this, QLatin1String(
"text"));
48 static_assert(
int(QSGTextNode::Normal) ==
int(QQuickText::Normal));
49 static_assert(
int(QSGTextNode::Outline) ==
int(QQuickText::Outline));
50 static_assert(
int(QSGTextNode::Raised) ==
int(QQuickText::Raised));
51 static_assert(
int(QSGTextNode::Sunken) ==
int(QQuickText::Sunken));
53 static_assert(
int(QSGTextNode::QtRendering) ==
int(QQuickText::QtRendering));
54 static_assert(
int(QSGTextNode::NativeRendering) ==
int(QQuickText::NativeRendering));
55 static_assert(
int(QSGTextNode::CurveRendering) ==
int(QQuickText::CurveRendering));
58QSGInternalTextNode::~QSGInternalTextNode()
60 qDeleteAll(m_textures);
63QSGGlyphNode *QSGInternalTextNode::addGlyphs(
const QPointF &position,
64 const QGlyphRun &glyphs,
66 RecycleBin *recycleBin,
67 QQuickText::TextStyle style,
68 const QColor &styleColor,
71 QRawFont font = glyphs.rawFont();
73 QSGTextNode::RenderType preferredRenderType = m_renderType;
74 if (m_renderType != NativeRendering) {
75 if (
const QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine)
76 if (fe->hasUnreliableGlyphOutline() || !fe->isSmoothlyScalable)
77 preferredRenderType = QSGTextNode::NativeRendering;
80 if (preferredRenderType == NativeRendering)
81 m_containsUnscalableGlyphs =
true;
83 QSGGlyphNode *node = findOrCreateGlyphNode(preferredRenderType, recycleBin);
84 node->setRenderTypeQuality(m_renderTypeQuality);
85 node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
86 node->setStyle(style);
87 node->setStyleColor(styleColor);
88 node->setColor(color);
92
93
94
95
96
97 node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
98 node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
100 if (parentNode ==
nullptr)
103 if (node->parent() ==
nullptr)
104 parentNode->appendChildNode(node);
106 if (style == QQuickText::Outline && color.alpha() > 0 && styleColor != color) {
107 QSGGlyphNode *fillNode = findOrCreateGlyphNode(preferredRenderType, recycleBin);
108 fillNode->setRenderTypeQuality(m_renderTypeQuality);
109 fillNode->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
110 fillNode->setStyle(QQuickText::Normal);
111 fillNode->setPreferredAntialiasingMode(QSGGlyphNode::GrayAntialiasing);
112 fillNode->setColor(color);
115 fillNode->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
116 fillNode->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
118 if (fillNode->parent() ==
nullptr)
119 parentNode->appendChildNode(fillNode);
120 fillNode->setRenderOrder(node->renderOrder() + 1);
126void QSGInternalTextNode::setCursor(
const QRectF &rect,
const QColor &color, RecycleBin *recycleBin)
128 if (m_cursorNode ==
nullptr)
129 m_cursorNode = findOrCreateRectangleNode(recycleBin);
130 m_cursorNode->setRect(rect);
131 m_cursorNode->setColor(color);
132 m_cursorNode->update();
133 if (m_cursorNode->parent() ==
nullptr)
134 appendChildNode(m_cursorNode);
137void QSGInternalTextNode::clearCursor()
140 removeChildNode(m_cursorNode);
142 m_cursorNode =
nullptr;
145void QSGInternalTextNode::addDecorationNode(
const QRectF &rect,
147 QTextCharFormat::UnderlineStyle style,
148 RecycleBin *recycleBin)
151 addRectangleNode(rect, color, recycleBin);
154void QSGInternalTextNode::addRectangleNode(
const QRectF &rect,
156 RecycleBin *recycleBin)
158 QSGInternalRectangleNode *node = findOrCreateRectangleNode(recycleBin);
160 node->setColor(color);
163 if (node->parent() ==
nullptr)
164 appendChildNode(node);
167void QSGInternalTextNode::addImage(
const QRectF &rect,
const QImage &image, RecycleBin *recycleBin)
169 QSGInternalImageNode *node = findOrCreateImageNode(recycleBin);
170 QSGTexture *texture = m_renderContext->createTexture(image);
171 texture->setFiltering(m_filtering);
172 m_textures.append(texture);
173 node->setTargetRect(rect);
174 node->setInnerTargetRect(rect);
175 node->setTexture(texture);
176 node->setFiltering(m_filtering);
177 if (node->parent() ==
nullptr)
178 appendChildNode(node);
182void QSGInternalTextNode::doAddTextDocument(QPointF position,
183 QTextDocument *textDocument,
186 RecycleBin *recycleBin)
188 QQuickTextNodeEngine engine;
189 engine.setTextColor(m_color);
190 engine.setSelectedTextColor(m_selectionTextColor);
191 engine.setSelectionColor(m_selectionColor);
192 engine.setAnchorColor(m_linkColor);
193 engine.setPosition(position);
194 engine.setDevicePixelRatio(m_devicePixelRatio);
196 QList<QTextFrame *> frames;
197 frames.append(textDocument->rootFrame());
198 while (!frames.isEmpty()) {
199 QTextFrame *textFrame = frames.takeFirst();
200 frames.append(textFrame->childFrames());
202 engine.addFrameDecorations(textDocument, textFrame);
204 if (textFrame->firstPosition() > textFrame->lastPosition()
205 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
206 const int pos = textFrame->firstPosition() - 1;
207 auto *a =
static_cast<QtPrivate::ProtectedLayoutAccessor *>(textDocument->documentLayout());
208 QTextCharFormat format = a->formatAccessor(pos);
209 QRectF rect = a->frameBoundingRect(textFrame);
211 QTextBlock block = textFrame->firstCursorPosition().block();
212 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
213 engine.addTextObject(block, rect.topLeft(), format, QQuickTextNodeEngine::Unselected, textDocument,
214 pos, textFrame->frameFormat().position());
216 QTextFrame::iterator it = textFrame->begin();
218 while (!it.atEnd()) {
219 Q_ASSERT(!engine.currentLine().isValid());
221 QTextBlock block = it.currentBlock();
222 engine.addTextBlock(textDocument, block, position, m_color, m_linkColor, selectionStart, selectionEnd,
223 (textDocument->characterCount() > QQuickTextPrivate::largeTextSizeThreshold ?
224 m_viewport : QRectF()));
230 engine.addToSceneGraph(
this, recycleBin, QQuickText::TextStyle(m_textStyle), m_styleColor);
233void QSGInternalTextNode::doAddTextLayout(QPointF position,
234 QTextLayout *textLayout,
239 RecycleBin *recycleBin)
241 QQuickTextNodeEngine engine;
242 engine.setTextColor(m_color);
243 engine.setSelectedTextColor(m_selectionTextColor);
244 engine.setSelectionColor(m_selectionColor);
245 engine.setAnchorColor(m_linkColor);
246 engine.setPosition(position);
247 engine.setDevicePixelRatio(m_devicePixelRatio);
250 int preeditLength = textLayout->preeditAreaText().size();
251 int preeditPosition = textLayout->preeditAreaPosition();
254 QVarLengthArray<QTextLayout::FormatRange> colorChanges;
255 engine.mergeFormats(textLayout, &colorChanges);
257 lineCount = lineCount >= 0
258 ? qMin(lineStart + lineCount, textLayout->lineCount())
259 : textLayout->lineCount();
261 bool inViewport =
false;
262 for (
int i=lineStart; i<lineCount; ++i) {
263 QTextLine line = textLayout->lineAt(i);
265 int start = line.textStart();
266 int length = line.textLength();
267 int end = start + length;
270 if (preeditPosition >= 0
271 && preeditPosition >= start
272 && preeditPosition < end) {
273 end += preeditLength;
277 if (m_viewport.isNull() || (line.y() + line.height() > m_viewport.top() && line.y() < m_viewport.bottom())) {
278 if (!inViewport && !m_viewport.isNull()) {
279 m_firstLineInViewport = i;
280 qCDebug(lcVP) <<
"first line in viewport" << i <<
"@" << line.y();
283 engine.setCurrentLine(line);
284 engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd);
285 }
else if (inViewport) {
286 Q_ASSERT(!m_viewport.isNull());
287 m_firstLinePastViewport = i;
288 qCDebug(lcVP) <<
"first omitted line past bottom of viewport" << i <<
"@" << line.y();
293 engine.addToSceneGraph(
this, recycleBin, QQuickText::TextStyle(m_textStyle), m_styleColor);
298 class ChildNodeCollector :
public QSGNodeVisitorEx
301 ChildNodeCollector(QSGInternalTextNode::RecycleBin &recycleBin,
302 std::vector<QSGNode *> &untrackedNodes)
303 : m_recycleBin(recycleBin)
304 , m_untrackedNodes(untrackedNodes)
308 bool visit(QSGTransformNode *node)
override
311 m_untrackedNodes.push_back(node);
315 void endVisit(QSGTransformNode *)
override
319 bool visit(QSGClipNode *node) override
323 m_untrackedNodes.push_back(node);
327 void endVisit(QSGClipNode *) override
331 bool visit(QSGGeometryNode *node)
override
334 m_untrackedNodes.push_back(node);
338 void endVisit(QSGGeometryNode *)
override
342 bool visit(QSGOpacityNode *node)
override
345 m_untrackedNodes.push_back(node);
349 void endVisit(QSGOpacityNode *)
override
353 bool visit(QSGInternalImageNode *node) override
355 m_recycleBin.unusedImageNodes.append(node);
359 void endVisit(QSGInternalImageNode *) override
363 bool visit(QSGPainterNode *node)
override
366 m_untrackedNodes.push_back(node);
370 void endVisit(QSGPainterNode *)
override
374 bool visit(QSGInternalRectangleNode *node) override
376 m_recycleBin.unusedRectangleNodes.append(node);
380 void endVisit(QSGInternalRectangleNode *) override
384 bool visit(QSGGlyphNode *node)
override
390 if (node->childCount() > 0)
391 m_untrackedNodes.push_back(node);
393 m_recycleBin.unusedGlyphNodes.append(node);
398 void endVisit(QSGGlyphNode *)
override
402 bool visit(QSGRootNode *node)
override
405 m_untrackedNodes.push_back(node);
409 void endVisit(QSGRootNode *)
override
413#if QT_CONFIG(quick_sprite)
414 bool visit(QSGSpriteNode *node) override
417 m_untrackedNodes.push_back(node);
421 void endVisit(QSGSpriteNode *) override
425 bool visit(QSGRenderNode *node)
override
428 m_untrackedNodes.push_back(node);
432 void endVisit(QSGRenderNode *)
override
437 QSGInternalTextNode::RecycleBin &m_recycleBin;
438 std::vector<QSGNode *> &m_untrackedNodes;
442QSGInternalRectangleNode *QSGInternalTextNode::findOrCreateRectangleNode(RecycleBin *recycleBin)
444 qCDebug(lcTextRecycle) <<
"Searching for rectangle node";
446 if (recycleBin ==
nullptr || recycleBin->unusedRectangleNodes.isEmpty()) {
447 qCDebug(lcTextRecycle) <<
" Creating new rectangle node, no suitable node found";
448 return m_renderContext->sceneGraphContext()->createInternalRectangleNode();
451 qCDebug(lcTextRecycle) <<
" Found node, recycling";
452 auto *node = recycleBin->unusedRectangleNodes.last();
453 recycleBin->unusedRectangleNodes.removeLast();
458QSGInternalImageNode *QSGInternalTextNode::findOrCreateImageNode(RecycleBin *recycleBin)
460 qCDebug(lcTextRecycle) <<
"Searching for image node";
462 if (recycleBin ==
nullptr || recycleBin->unusedImageNodes.isEmpty()) {
463 qCDebug(lcTextRecycle) <<
" Creating new image node, no suitable node found";
464 return m_renderContext->sceneGraphContext()->createInternalImageNode(m_renderContext);
467 qCDebug(lcTextRecycle) <<
" Found node, recycling";
468 auto *node = recycleBin->unusedImageNodes.last();
469 recycleBin->unusedImageNodes.removeLast();
473QSGGlyphNode *QSGInternalTextNode::findOrCreateGlyphNode(RenderType renderType,
474 RecycleBin *recycleBin)
476 qCDebug(lcTextRecycle) <<
"Searching for glyph node with renderType" << renderType;
477 renderType = m_renderContext->sceneGraphContext()->processTextRenderType(renderType);
479 if (recycleBin !=
nullptr) {
480 for (
int i = recycleBin->unusedGlyphNodes.size() - 1; i >= 0; --i) {
481 QSGGlyphNode *n = recycleBin->unusedGlyphNodes.at(i);
482 if (n->renderType() == renderType) {
483 qCDebug(lcTextRecycle) <<
" Found node, recycling";
484 recycleBin->unusedGlyphNodes.removeAt(i);
487 qCDebug(lcTextRecycle) <<
" Unsuitable node found:" << n->renderType();
492 qCDebug(lcTextRecycle) <<
" Creating new glyph node, no suitable node found";
493 return m_renderContext->sceneGraphContext()->createGlyphNode(m_renderContext,
497void QSGInternalTextNode::clear()
499 while (firstChild() !=
nullptr)
501 m_cursorNode =
nullptr;
502 qDeleteAll(m_textures);
506void QSGInternalTextNode::recycle(RecycleBin *recycleBin)
508 Q_ASSERT(recycleBin !=
nullptr);
509 Q_ASSERT(recycleBin->unusedGlyphNodes.isEmpty());
510 Q_ASSERT(recycleBin->unusedImageNodes.isEmpty());
511 Q_ASSERT(recycleBin->unusedRectangleNodes.isEmpty());
513 recycleBin->unusedGlyphNodes.reserve(childCount());
515 std::vector<QSGNode *> untrackedNodes;
516 ChildNodeCollector collector(*recycleBin, untrackedNodes);
524 QSGNode *node = firstChild();
525 while (node !=
nullptr) {
526 if (node->type() == QSGNode::GeometryNodeType
527 && node->flags().testFlag(QSGNode::IsVisitableNode)) {
528 QSGVisitableNode *visitableNode =
static_cast<QSGVisitableNode *>(node);
529 visitableNode->accept(&collector);
531 untrackedNodes.push_back(node);
534 node = node->nextSibling();
537 for (QSGGlyphNode *glyphNode : recycleBin->unusedGlyphNodes)
538 glyphNode->recycle();
540 qDeleteAll(untrackedNodes);
542#if defined(QSGINTERNALTEXTNODE_NO_RECYCLE)
543 qDeleteAll(recycleBin->unusedGlyphNodes);
544 recycleBin->unusedGlyphNodes = {};
546 qDeleteAll(recycleBin->unusedImageNodes);
547 recycleBin->unusedImageNodes = {};
549 qDeleteAll(recycleBin->unusedRectangleNodes);
550 recycleBin->unusedRectangleNodes = {};
552 qDeleteAll(m_textures);
554 recycleBin->unusedTextures = m_textures;
557 qCDebug(lcTextRecycle) <<
"Recycle: "
558 << recycleBin->unusedGlyphNodes.size() <<
"glyph nodes"
559 << recycleBin->unusedImageNodes.size() <<
"image nodes"
560 << recycleBin->unusedRectangleNodes.size() <<
"rectangle nodes"
561 << recycleBin->unusedTextures.size() <<
"textures";
564 m_cursorNode =
nullptr;
565 recycleBin->unusedGlyphNodes.squeeze();
568void QSGInternalTextNode::discardUnusedNodes(RecycleBin *recycleBin)
570 qCDebug(lcTextRecycle) <<
"Discard: "
571 << recycleBin->unusedGlyphNodes.size() <<
"glyph nodes"
572 << recycleBin->unusedImageNodes.size() <<
"image nodes"
573 << recycleBin->unusedRectangleNodes.size() <<
"rectangle nodes"
574 << recycleBin->unusedTextures.size() <<
"textures";
576 qDeleteAll(recycleBin->unusedGlyphNodes);
577 recycleBin->unusedGlyphNodes = {};
579 qDeleteAll(recycleBin->unusedImageNodes);
580 recycleBin->unusedImageNodes = {};
582 qDeleteAll(recycleBin->unusedRectangleNodes);
583 recycleBin->unusedRectangleNodes = {};
585 qDeleteAll(recycleBin->unusedTextures);
586 recycleBin->unusedTextures = {};
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)