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 #include "qsgdefaultdistancefieldglyphcache_p.h"
44 #include <QtGui/private/qdistancefield_p.h>
45 #include <QtQuick/private/qsgdistancefieldutil_p.h>
46 #include <qopenglfunctions.h>
52 QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
53 : QSGDistanceFieldGlyphCache(man, c, font)
55 , m_maxTextureCount(3)
59 m_blitVertexCoordinateArray[0] = -1.0f;
60 m_blitVertexCoordinateArray[1] = -1.0f;
61 m_blitVertexCoordinateArray[2] = 1.0f;
62 m_blitVertexCoordinateArray[3] = -1.0f;
63 m_blitVertexCoordinateArray[4] = 1.0f;
64 m_blitVertexCoordinateArray[5] = 1.0f;
65 m_blitVertexCoordinateArray[6] = -1.0f;
66 m_blitVertexCoordinateArray[7] = 1.0f;
68 m_blitTextureCoordinateArray[0] = 0.0f;
69 m_blitTextureCoordinateArray[1] = 0.0f;
70 m_blitTextureCoordinateArray[2] = 1.0f;
71 m_blitTextureCoordinateArray[3] = 0.0f;
72 m_blitTextureCoordinateArray[4] = 1.0f;
73 m_blitTextureCoordinateArray[5] = 1.0f;
74 m_blitTextureCoordinateArray[6] = 0.0f;
75 m_blitTextureCoordinateArray[7] = 1.0f;
77 m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
80 QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache()
82 for (int i = 0; i < m_textures.count(); ++i)
83 glDeleteTextures(1, &m_textures[i].texture);
84 ctx->functions()->glDeleteFramebuffers(1, &m_fbo);
86 delete m_areaAllocator;
89 void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
91 QList<GlyphPosition> glyphPositions;
92 QVector<glyph_t> glyphsToRender;
94 for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
95 glyph_t glyphIndex = *it;
97 int glyphWidth = qCeil(glyphData(glyphIndex).boundingRect.width()) + distanceFieldRadius() * 2;
98 QSize glyphSize(glyphWidth, QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()));
99 QRect alloc = m_areaAllocator->allocate(glyphSize);
101 if (alloc.isNull()) {
102 // Unallocate unused glyphs until we can allocated the new glyph
103 while (alloc.isNull() && !m_unusedGlyphs.isEmpty()) {
104 glyph_t unusedGlyph = *m_unusedGlyphs.constBegin();
106 TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
107 int unusedGlyphWidth = qCeil(glyphData(unusedGlyph).boundingRect.width()) + distanceFieldRadius() * 2;
108 m_areaAllocator->deallocate(QRect(unusedCoord.x, unusedCoord.y, unusedGlyphWidth, QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution())));
110 m_unusedGlyphs.remove(unusedGlyph);
111 m_glyphsTexture.remove(unusedGlyph);
112 removeGlyph(unusedGlyph);
114 alloc = m_areaAllocator->allocate(glyphSize);
117 // Not enough space left for this glyph... skip to the next one
122 TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize());
123 alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height());
124 tex->allocatedArea |= alloc;
127 p.glyph = glyphIndex;
128 p.position = alloc.topLeft();
130 glyphPositions.append(p);
131 glyphsToRender.append(glyphIndex);
132 m_glyphsTexture.insert(glyphIndex, tex);
135 setGlyphsPosition(glyphPositions);
136 markGlyphsToRender(glyphsToRender);
139 void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
141 QHash<TextureInfo *, QVector<glyph_t> > glyphTextures;
143 QHash<glyph_t, QImage>::const_iterator it;
144 for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) {
145 glyph_t glyphIndex = it.key();
146 TexCoord c = glyphTexCoord(glyphIndex);
147 TextureInfo *texInfo = m_glyphsTexture.value(glyphIndex);
149 resizeTexture(texInfo, texInfo->allocatedArea.width(), texInfo->allocatedArea.height());
150 glBindTexture(GL_TEXTURE_2D, texInfo->texture);
152 glyphTextures[texInfo].append(glyphIndex);
154 QImage glyph = it.value();
155 int expectedWidth = qCeil(c.width + c.xMargin * 2);
156 if (glyph.width() != expectedWidth)
157 glyph = glyph.copy(0, 0, expectedWidth, glyph.height());
159 if (useWorkaroundBrokenFBOReadback()) {
160 uchar *inBits = glyph.scanLine(0);
161 uchar *outBits = texInfo->image.scanLine(int(c.y)) + int(c.x);
162 for (int y = 0; y < glyph.height(); ++y) {
163 memcpy(outBits, inBits, glyph.width());
164 inBits += glyph.bytesPerLine();
165 outBits += texInfo->image.bytesPerLine();
169 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits());
172 QHash<TextureInfo *, QVector<glyph_t> >::const_iterator i;
173 for (i = glyphTextures.constBegin(); i != glyphTextures.constEnd(); ++i) {
175 t.textureId = i.key()->texture;
176 t.size = i.key()->size;
177 setGlyphsTexture(i.value(), t);
181 void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
183 m_unusedGlyphs -= glyphs;
186 void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
188 m_unusedGlyphs += glyphs;
191 void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height)
193 if (useWorkaroundBrokenFBOReadback() && texInfo->image.isNull())
194 texInfo->image = QImage(width, height, QImage::Format_Indexed8);
196 while (glGetError() != GL_NO_ERROR) { }
198 glGenTextures(1, &texInfo->texture);
199 glBindTexture(GL_TEXTURE_2D, texInfo->texture);
201 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
202 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
203 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
204 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
205 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
207 texInfo->size = QSize(width, height);
209 GLuint error = glGetError();
210 if (error != GL_NO_ERROR) {
211 glBindTexture(GL_TEXTURE_2D, 0);
212 glDeleteTextures(1, &texInfo->texture);
213 texInfo->texture = 0;
218 void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
220 int oldWidth = texInfo->size.width();
221 int oldHeight = texInfo->size.height();
222 if (width == oldWidth && height == oldHeight)
225 GLuint oldTexture = texInfo->texture;
226 createTexture(texInfo, width, height);
231 updateTexture(oldTexture, texInfo->texture, texInfo->size);
233 if (useWorkaroundBrokenFBOReadback()) {
234 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, texInfo->image.constBits());
235 texInfo->image = texInfo->image.copy(0, 0, width, height);
236 glDeleteTextures(1, &oldTexture);
243 Q_ASSERT(m_blitProgram);
246 ctx->functions()->glGenFramebuffers(1, &m_fbo);
247 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
250 glGenTextures(1, &tmp_texture);
251 glBindTexture(GL_TEXTURE_2D, tmp_texture);
252 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
253 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
254 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
257 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
258 glBindTexture(GL_TEXTURE_2D, 0);
259 ctx->functions()->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
260 GL_TEXTURE_2D, tmp_texture, 0);
262 ctx->functions()->glActiveTexture(GL_TEXTURE0);
263 glBindTexture(GL_TEXTURE_2D, oldTexture);
265 // save current render states
266 GLboolean stencilTestEnabled;
267 GLboolean depthTestEnabled;
268 GLboolean scissorTestEnabled;
269 GLboolean blendEnabled;
272 glGetBooleanv(GL_STENCIL_TEST, &stencilTestEnabled);
273 glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled);
274 glGetBooleanv(GL_SCISSOR_TEST, &scissorTestEnabled);
275 glGetBooleanv(GL_BLEND, &blendEnabled);
276 glGetIntegerv(GL_VIEWPORT, &viewport[0]);
277 glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgram);
279 glDisable(GL_STENCIL_TEST);
280 glDisable(GL_DEPTH_TEST);
281 glDisable(GL_SCISSOR_TEST);
284 glViewport(0, 0, oldWidth, oldHeight);
286 ctx->functions()->glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_blitVertexCoordinateArray);
287 ctx->functions()->glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_blitTextureCoordinateArray);
289 m_blitProgram->bind();
290 m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
291 m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
292 m_blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR));
293 m_blitProgram->setUniformValue("imageTexture", GLuint(0));
295 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
297 glBindTexture(GL_TEXTURE_2D, texInfo->texture);
299 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
301 ctx->functions()->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
303 glDeleteTextures(1, &tmp_texture);
304 glDeleteTextures(1, &oldTexture);
306 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
308 // restore render states
309 if (stencilTestEnabled)
310 glEnable(GL_STENCIL_TEST);
311 if (depthTestEnabled)
312 glEnable(GL_DEPTH_TEST);
313 if (scissorTestEnabled)
314 glEnable(GL_SCISSOR_TEST);
317 glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
318 ctx->functions()->glUseProgram(oldProgram);
320 m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
321 m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
324 bool QSGDefaultDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const
326 static bool set = false;
327 static bool useWorkaround = false;
329 QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(ctx));
330 useWorkaround = ctx_p->workaround_brokenFBOReadBack;
333 return useWorkaround;
336 int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
338 if (!m_maxTextureSize)
339 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
340 return m_maxTextureSize;