1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #define EGL_EGLEXT_PROTOTYPES
43 #define GL_GLEXT_PROTOTYPES
44 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
45 #include <GLES2/gl2.h>
46 #include <GLES2/gl2ext.h>
49 #include "qsgshareddistancefieldglyphcache_p.h"
51 #include <QtCore/qhash.h>
52 #include <QtCore/qthread.h>
53 #include <QtGui/qplatformsharedgraphicscache_qpa.h>
55 #include <QtQuick/qquickcanvas.h>
57 #include <QtOpenGL/qglframebufferobject.h>
59 // #define QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG
61 Q_DECLARE_METATYPE(QVector<quint32>)
62 Q_DECLARE_METATYPE(QVector<QImage>)
66 QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId,
67 QPlatformSharedGraphicsCache *sharedGraphicsCache,
68 QSGDistanceFieldGlyphCacheManager *man,
71 : QSGDistanceFieldGlyphCache(man, c, font)
73 , m_sharedGraphicsCache(sharedGraphicsCache)
75 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
76 qDebug("QSGSharedDistanceFieldGlyphCache with id %s created in thread %p",
77 cacheId.constData(), QThread::currentThreadId());
80 Q_ASSERT(sizeof(glyph_t) == sizeof(quint32));
81 Q_ASSERT(sharedGraphicsCache != 0);
83 qRegisterMetaType<QVector<quint32> >();
84 qRegisterMetaType<QVector<QImage> >();
86 connect(sharedGraphicsCache, SIGNAL(itemsMissing(QByteArray,QVector<quint32>)),
87 this, SLOT(reportItemsMissing(QByteArray,QVector<quint32>)),
88 Qt::DirectConnection);
89 connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
90 this, SLOT(reportItemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
91 Qt::DirectConnection);
92 connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
93 this, SLOT(reportItemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
94 Qt::DirectConnection);
95 connect(sharedGraphicsCache, SIGNAL(itemsInvalidated(QByteArray,QVector<quint32>)),
96 this, SLOT(reportItemsInvalidated(QByteArray,QVector<quint32>)),
97 Qt::DirectConnection);
100 QSGSharedDistanceFieldGlyphCache::~QSGSharedDistanceFieldGlyphCache()
103 QHash<glyph_t, void *>::const_iterator it = m_bufferForGlyph.constBegin();
104 while (it != m_bufferForGlyph.constEnd()) {
105 m_sharedGraphicsCache->dereferenceBuffer(it.value());
111 QHash<quint32, PendingGlyph>::const_iterator it = m_pendingReadyGlyphs.constBegin();
112 while (it != m_pendingReadyGlyphs.constEnd()) {
113 m_sharedGraphicsCache->dereferenceBuffer(it.value().buffer);
119 void QSGSharedDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
121 QMutexLocker locker(&m_pendingGlyphsMutex);
123 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
124 qDebug("QSGSharedDistanceFieldGlyphCache::requestGlyphs() called for %s (%d glyphs)",
125 m_cacheId.constData(), glyphs.size());
128 m_requestedGlyphsThatHaveNotBeenReturned.unite(glyphs);
129 m_requestedGlyphs.unite(glyphs);
131 QVector<quint32> glyphsVector;
132 glyphsVector.reserve(glyphs.size());
134 QSet<glyph_t>::const_iterator it;
135 for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) {
136 Q_ASSERT(!m_bufferForGlyph.contains(*it));
137 glyphsVector.append(*it);
140 // Invoke method on queued connection to make sure it's called asynchronously on the
141 // correct thread (requestGlyphs() is called from the rendering thread.)
142 QMetaObject::invokeMethod(m_sharedGraphicsCache, "requestItems", Qt::QueuedConnection,
143 Q_ARG(QByteArray, m_cacheId),
144 Q_ARG(QVector<quint32>, glyphsVector));
147 void QSGSharedDistanceFieldGlyphCache::waitForGlyphs()
150 QMutexLocker locker(&m_pendingGlyphsMutex);
151 while (!m_requestedGlyphsThatHaveNotBeenReturned.isEmpty())
152 m_pendingGlyphsCondition.wait(&m_pendingGlyphsMutex);
156 void QSGSharedDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
159 QMutexLocker locker(&m_pendingGlyphsMutex);
160 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
161 qDebug("QSGSharedDistanceFieldGlyphCache::storeGlyphs() called for %s (%d glyphs)",
162 m_cacheId.constData(), glyphs.size());
165 int glyphCount = glyphs.size();
166 QVector<quint32> glyphIds(glyphCount);
167 QVector<QImage> images(glyphCount);
168 QHash<glyph_t, QImage>::const_iterator it = glyphs.constBegin();
170 while (it != glyphs.constEnd()) {
171 m_requestedGlyphsThatHaveNotBeenReturned.insert(it.key());
172 glyphIds[i] = it.key();
173 images[i] = it.value();
178 QMetaObject::invokeMethod(m_sharedGraphicsCache, "insertItems", Qt::QueuedConnection,
179 Q_ARG(QByteArray, m_cacheId),
180 Q_ARG(QVector<quint32>, glyphIds),
181 Q_ARG(QVector<QImage>, images));
184 processPendingGlyphs();
187 void QSGSharedDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
191 // Intentionally empty. Not required in this implementation, since the glyphs are reference
192 // counted outside and releaseGlyphs() will only be called when there are no more references.
195 void QSGSharedDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
197 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
198 qDebug("QSGSharedDistanceFieldGlyphCache::releaseGlyphs() called for %s (%d glyphs)",
199 m_cacheId.constData(), glyphs.size());
202 m_requestedGlyphs.subtract(glyphs);
204 QVector<quint32> glyphsVector;
205 glyphsVector.reserve(glyphs.size());
207 QSet<glyph_t>::const_iterator glyphsIt;
208 for (glyphsIt = glyphs.constBegin(); glyphsIt != glyphs.constEnd(); ++glyphsIt) {
209 QHash<glyph_t, void *>::iterator bufferIt = m_bufferForGlyph.find(*glyphsIt);
210 if (bufferIt != m_bufferForGlyph.end()) {
211 void *buffer = bufferIt.value();
212 removeGlyph(*glyphsIt);
213 m_bufferForGlyph.erase(bufferIt);
214 Q_ASSERT(!m_bufferForGlyph.contains(*glyphsIt));
216 if (!m_sharedGraphicsCache->dereferenceBuffer(buffer)) {
217 #if !defined(QT_NO_DEBUG)
218 bufferIt = m_bufferForGlyph.begin();
219 while (bufferIt != m_bufferForGlyph.end()) {
220 Q_ASSERT(bufferIt.value() != buffer);
227 glyphsVector.append(*glyphsIt);
230 QMetaObject::invokeMethod(m_sharedGraphicsCache, "releaseItems", Qt::QueuedConnection,
231 Q_ARG(QByteArray, m_cacheId),
232 Q_ARG(QVector<quint32>, glyphsVector));
235 void QSGSharedDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
237 Owner &owner = m_registeredOwners[ownerElement];
238 if (owner.ref == 0) {
239 owner.item = ownerElement;
241 bool ok = connect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
242 Q_ASSERT_X(ok, Q_FUNC_INFO, "QML element that owns a glyph node must have triggerPreprocess() slot");
248 void QSGSharedDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
250 QHash<QQuickItem *, Owner>::iterator it = m_registeredOwners.find(ownerElement);
251 if (it != m_registeredOwners.end() && --it->ref <= 0) {
253 disconnect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess()));
254 m_registeredOwners.erase(it);
258 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
259 # include <QtOpenGL/private/qglextensions_p.h>
261 void QSGSharedDistanceFieldGlyphCache::saveTexture(GLuint textureId, int width, int height)
264 glGenFramebuffers(1, &fboId);
266 GLuint tmpTexture = 0;
267 glGenTextures(1, &tmpTexture);
268 glBindTexture(GL_TEXTURE_2D, tmpTexture);
269 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
270 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
271 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
272 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
273 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
274 glBindTexture(GL_TEXTURE_2D, 0);
276 glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
277 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
280 glActiveTexture(GL_TEXTURE0);
281 glBindTexture(GL_TEXTURE_2D, textureId);
283 glDisable(GL_STENCIL_TEST);
284 glDisable(GL_DEPTH_TEST);
285 glDisable(GL_SCISSOR_TEST);
288 GLfloat textureCoordinateArray[8];
289 textureCoordinateArray[0] = 0.0f;
290 textureCoordinateArray[1] = 0.0f;
291 textureCoordinateArray[2] = 1.0f;
292 textureCoordinateArray[3] = 0.0f;
293 textureCoordinateArray[4] = 1.0f;
294 textureCoordinateArray[5] = 1.0f;
295 textureCoordinateArray[6] = 0.0f;
296 textureCoordinateArray[7] = 1.0f;
298 GLfloat vertexCoordinateArray[8];
299 vertexCoordinateArray[0] = -1.0f;
300 vertexCoordinateArray[1] = -1.0f;
301 vertexCoordinateArray[2] = 1.0f;
302 vertexCoordinateArray[3] = -1.0f;
303 vertexCoordinateArray[4] = 1.0f;
304 vertexCoordinateArray[5] = 1.0f;
305 vertexCoordinateArray[6] = -1.0f;
306 vertexCoordinateArray[7] = 1.0f;
308 glViewport(0, 0, width, height);
309 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray);
310 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray);
313 static const char *vertexShaderSource =
314 "attribute highp vec4 vertexCoordsArray; \n"
315 "attribute highp vec2 textureCoordArray; \n"
316 "varying highp vec2 textureCoords; \n"
319 " gl_Position = vertexCoordsArray; \n"
320 " textureCoords = textureCoordArray; \n"
323 static const char *fragmentShaderSource =
324 "varying highp vec2 textureCoords; \n"
325 "uniform sampler2D texture; \n"
328 " gl_FragColor = texture2D(texture, textureCoords); \n"
331 GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
332 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
334 if (vertexShader == 0 || fragmentShader == 0) {
335 GLenum error = glGetError();
336 qWarning("SharedGraphicsCacheServer::setupShaderPrograms: Failed to create shaders. (GL error: %x)",
341 glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
342 glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
343 glCompileShader(vertexShader);
346 glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &len);
349 glGetShaderInfoLog(vertexShader, 2048, NULL, infoLog);
350 if (qstrlen(infoLog) > 0) {
351 qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling vertex shader:\n %s",
356 glCompileShader(fragmentShader);
357 glGetShaderInfoLog(fragmentShader, 2048, NULL, infoLog);
358 if (qstrlen(infoLog) > 0) {
359 qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems compiling fragent shader:\n %s",
364 GLuint shaderProgram = glCreateProgram();
365 glAttachShader(shaderProgram, vertexShader);
366 glAttachShader(shaderProgram, fragmentShader);
368 glBindAttribLocation(shaderProgram, 0, "vertexCoordsArray");
369 glBindAttribLocation(shaderProgram, 1, "textureCoordArray");
371 glLinkProgram(shaderProgram);
372 glGetProgramInfoLog(shaderProgram, 2048, NULL, infoLog);
373 if (qstrlen(infoLog) > 0) {
374 qWarning("SharedGraphicsCacheServer::setupShaderPrograms, problems linking shaders:\n %s",
379 glUseProgram(shaderProgram);
380 glEnableVertexAttribArray(0);
381 glEnableVertexAttribArray(1);
383 int textureUniformLocation = glGetUniformLocation(shaderProgram, "texture");
384 glUniform1i(textureUniformLocation, 0);
387 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
390 GLenum error = glGetError();
391 if (error != GL_NO_ERROR) {
392 qWarning("SharedGraphicsCacheServer::readBackBuffer: glDrawArrays reported error 0x%x",
397 uchar *data = new uchar[width * height * 4];
399 glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
401 QImage image(width, height, QImage::Format_ARGB32);
402 quint32 *dest = reinterpret_cast<quint32 *>(image.bits());
403 for (int i=0; i<width*height; ++i)
404 dest[i] = qRgba(0xff, 0xff, 0xff, data[i]);
406 QByteArray fileName = m_cacheId + " " + QByteArray::number(textureId);
407 fileName = fileName.replace('/', '_').replace(' ', '_') + ".png";
408 image.save(QString::fromLocal8Bit(fileName));
411 GLenum error = glGetError();
412 if (error != GL_NO_ERROR) {
413 qWarning("SharedGraphicsCacheServer::readBackBuffer: glReadPixels reported error 0x%x",
418 glDisableVertexAttribArray(0);
419 glDisableVertexAttribArray(1);
421 glDeleteFramebuffers(1, &fboId);
422 glDeleteTextures(1, &tmpTexture);
429 struct TextureContent {
431 QVector<glyph_t> glyphs;
435 void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs()
437 Q_ASSERT(QThread::currentThread() == thread());
442 QMutexLocker locker(&m_pendingGlyphsMutex);
443 if (m_pendingMissingGlyphs.isEmpty()
444 && m_pendingReadyGlyphs.isEmpty()
445 && m_pendingInvalidatedGlyphs.isEmpty()) {
450 QVector<glyph_t> pendingMissingGlyphs;
451 pendingMissingGlyphs.reserve(m_pendingMissingGlyphs.size());
453 QSet<glyph_t>::const_iterator it = m_pendingMissingGlyphs.constBegin();
454 while (it != m_pendingMissingGlyphs.constEnd()) {
455 pendingMissingGlyphs.append(*it);
459 markGlyphsToRender(pendingMissingGlyphs);
463 QVector<glyph_t> filteredPendingInvalidatedGlyphs;
464 filteredPendingInvalidatedGlyphs.reserve(m_pendingInvalidatedGlyphs.size());
466 QSet<glyph_t>::const_iterator it = m_pendingInvalidatedGlyphs.constBegin();
467 while (it != m_pendingInvalidatedGlyphs.constEnd()) {
468 bool rerequestGlyph = false;
470 // The glyph was invalidated right after being posted as ready, we throw away
471 // the ready glyph and rerequest it to be certain
472 QHash<quint32, PendingGlyph>::iterator pendingGlyphIt = m_pendingReadyGlyphs.find(*it);
473 if (pendingGlyphIt != m_pendingReadyGlyphs.end()) {
474 m_sharedGraphicsCache->dereferenceBuffer(pendingGlyphIt.value().buffer);
475 pendingGlyphIt = m_pendingReadyGlyphs.erase(pendingGlyphIt);
476 rerequestGlyph = true;
479 void *bufferId = m_bufferForGlyph.value(*it, 0);
481 m_sharedGraphicsCache->dereferenceBuffer(bufferId);
482 m_bufferForGlyph.remove(*it);
483 rerequestGlyph = true;
487 filteredPendingInvalidatedGlyphs.append(*it);
492 // If this cache is still using the glyphs, reset the texture held by them, and mark them
493 // to be rendered again since they are still needed.
494 if (!filteredPendingInvalidatedGlyphs.isEmpty()) {
495 setGlyphsTexture(filteredPendingInvalidatedGlyphs, Texture());
496 markGlyphsToRender(filteredPendingInvalidatedGlyphs);
501 QList<GlyphPosition> glyphPositions;
503 QHash<void *, TextureContent> textureContentForBuffer;
505 QHash<quint32, PendingGlyph>::iterator it = m_pendingReadyGlyphs.begin();
506 while (it != m_pendingReadyGlyphs.end()) {
507 void *currentGlyphBuffer = m_bufferForGlyph.value(it.key(), 0);
508 if (currentGlyphBuffer != 0) {
509 if (!m_sharedGraphicsCache->dereferenceBuffer(currentGlyphBuffer)) {
510 Q_ASSERT(!textureContentForBuffer.contains(currentGlyphBuffer));
514 PendingGlyph &pendingGlyph = it.value();
516 // We don't ref or deref the buffer here, since it was already referenced when
517 // added to the pending ready glyphs
518 m_bufferForGlyph[it.key()] = pendingGlyph.buffer;
520 textureContentForBuffer[pendingGlyph.buffer].size = pendingGlyph.bufferSize;
521 textureContentForBuffer[pendingGlyph.buffer].glyphs.append(it.key());
523 GlyphPosition glyphPosition;
524 glyphPosition.glyph = it.key();
525 glyphPosition.position = pendingGlyph.position;
527 glyphPositions.append(glyphPosition);
533 setGlyphsPosition(glyphPositions);
536 QHash<void *, TextureContent>::const_iterator it = textureContentForBuffer.constBegin();
537 while (it != textureContentForBuffer.constEnd()) {
539 texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key());
540 texture.size = m_sharedGraphicsCache->sizeOfBuffer(it.key());
542 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
543 saveTexture(texture.textureId, texture.size.width(), texture.size.height());
545 setGlyphsTexture(it.value().glyphs, texture);
552 m_pendingMissingGlyphs.clear();
553 m_pendingInvalidatedGlyphs.clear();
554 m_pendingReadyGlyphs.clear();
558 void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId,
560 const QVector<quint32> &itemIds,
561 const QVector<QPoint> &positions)
563 bool requestedItemsInList = false;
565 QMutexLocker locker(&m_pendingGlyphsMutex);
566 if (m_cacheId != cacheId)
569 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
570 qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs)",
571 cacheId.constData(), itemIds.size());
574 for (int i=0; i<itemIds.size(); ++i) {
575 if (m_requestedGlyphsThatHaveNotBeenReturned.contains(itemIds.at(i))) {
576 requestedItemsInList = true;
582 if (requestedItemsInList)
583 reportItemsUpdated(cacheId, bufferId,itemIds, positions);
586 void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cacheId,
588 const QVector<quint32> &itemIds,
589 const QVector<QPoint> &positions)
592 QMutexLocker locker(&m_pendingGlyphsMutex);
593 if (m_cacheId != cacheId)
596 Q_ASSERT(itemIds.size() == positions.size());
598 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
599 qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs)",
600 cacheId.constData(), itemIds.size());
603 for (int i=0; i<itemIds.size(); ++i) {
604 if (m_requestedGlyphs.contains(itemIds.at(i))) {
605 PendingGlyph &pendingGlyph = m_pendingReadyGlyphs[itemIds.at(i)];
606 void *oldBuffer = pendingGlyph.buffer;
608 pendingGlyph.buffer = bufferId;
609 pendingGlyph.position = positions.at(i);
611 m_sharedGraphicsCache->referenceBuffer(bufferId);
613 m_sharedGraphicsCache->dereferenceBuffer(oldBuffer);
615 m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i));
620 m_pendingGlyphsCondition.wakeAll();
621 emit glyphsPending();
624 void QSGSharedDistanceFieldGlyphCache::reportItemsInvalidated(const QByteArray &cacheId,
625 const QVector<quint32> &itemIds)
628 QMutexLocker locker(&m_pendingGlyphsMutex);
629 if (m_cacheId != cacheId)
632 for (int i=0; i<itemIds.size(); ++i) {
633 if (m_requestedGlyphs.contains(itemIds.at(i)))
634 m_pendingInvalidatedGlyphs.insert(itemIds.at(i));
638 emit glyphsPending();
642 void QSGSharedDistanceFieldGlyphCache::reportItemsMissing(const QByteArray &cacheId,
643 const QVector<quint32> &itemIds)
646 QMutexLocker locker(&m_pendingGlyphsMutex);
647 if (m_cacheId != cacheId)
650 #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
651 qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsMissing() called for %s (%d glyphs)",
652 cacheId.constData(), itemIds.size());
655 for (int i=0; i<itemIds.size(); ++i) {
656 if (m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i)))
657 m_pendingMissingGlyphs.insert(itemIds.at(i));
661 m_pendingGlyphsCondition.wakeAll();
662 emit glyphsPending();