1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qsgdefaultdistancefieldglyphcache_p.h"
44 #include <QtGui/private/qdistancefield_p.h>
45 #include <QtGui/private/qopenglcontext_p.h>
46 #include <QtQuick/private/qsgdistancefieldutil_p.h>
47 #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);
89 delete m_areaAllocator;
92 void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
94 QList<GlyphPosition> glyphPositions;
95 QVector<glyph_t> glyphsToRender;
97 for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
98 glyph_t glyphIndex = *it;
100 int glyphWidth = qCeil(glyphData(glyphIndex).boundingRect.width()) + distanceFieldRadius() * 2;
101 QSize glyphSize(glyphWidth, QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()));
102 QRect alloc = m_areaAllocator->allocate(glyphSize);
104 if (alloc.isNull()) {
105 // Unallocate unused glyphs until we can allocated the new glyph
106 while (alloc.isNull() && !m_unusedGlyphs.isEmpty()) {
107 glyph_t unusedGlyph = *m_unusedGlyphs.constBegin();
109 TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
110 int unusedGlyphWidth = qCeil(glyphData(unusedGlyph).boundingRect.width()) + distanceFieldRadius() * 2;
111 m_areaAllocator->deallocate(QRect(unusedCoord.x, unusedCoord.y, unusedGlyphWidth, QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution())));
113 m_unusedGlyphs.remove(unusedGlyph);
114 m_glyphsTexture.remove(unusedGlyph);
115 removeGlyph(unusedGlyph);
117 alloc = m_areaAllocator->allocate(glyphSize);
120 // Not enough space left for this glyph... skip to the next one
125 TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize());
126 alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height());
127 tex->allocatedArea |= alloc;
130 p.glyph = glyphIndex;
131 p.position = alloc.topLeft();
133 glyphPositions.append(p);
134 glyphsToRender.append(glyphIndex);
135 m_glyphsTexture.insert(glyphIndex, tex);
138 setGlyphsPosition(glyphPositions);
139 markGlyphsToRender(glyphsToRender);
142 void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
144 QHash<TextureInfo *, QVector<glyph_t> > glyphTextures;
146 QHash<glyph_t, QImage>::const_iterator it;
147 for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) {
148 glyph_t glyphIndex = it.key();
149 TexCoord c = glyphTexCoord(glyphIndex);
150 TextureInfo *texInfo = m_glyphsTexture.value(glyphIndex);
152 resizeTexture(texInfo, texInfo->allocatedArea.width(), texInfo->allocatedArea.height());
153 glBindTexture(GL_TEXTURE_2D, texInfo->texture);
155 glyphTextures[texInfo].append(glyphIndex);
157 QImage glyph = it.value();
158 int expectedWidth = qCeil(c.width + c.xMargin * 2);
159 if (glyph.width() != expectedWidth)
160 glyph = glyph.copy(0, 0, expectedWidth, glyph.height());
162 if (useWorkaroundBrokenFBOReadback()) {
163 uchar *inBits = glyph.scanLine(0);
164 uchar *outBits = texInfo->image.scanLine(int(c.y)) + int(c.x);
165 for (int y = 0; y < glyph.height(); ++y) {
166 memcpy(outBits, inBits, glyph.width());
167 inBits += glyph.bytesPerLine();
168 outBits += texInfo->image.bytesPerLine();
172 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits());
175 QHash<TextureInfo *, QVector<glyph_t> >::const_iterator i;
176 for (i = glyphTextures.constBegin(); i != glyphTextures.constEnd(); ++i) {
178 t.textureId = i.key()->texture;
179 t.size = i.key()->size;
180 setGlyphsTexture(i.value(), t);
184 void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
186 m_unusedGlyphs -= glyphs;
189 void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
191 m_unusedGlyphs += glyphs;
194 void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height)
196 if (useWorkaroundBrokenFBOReadback() && texInfo->image.isNull())
197 texInfo->image = QImage(width, height, QImage::Format_Indexed8);
199 while (glGetError() != GL_NO_ERROR) { }
201 glGenTextures(1, &texInfo->texture);
202 glBindTexture(GL_TEXTURE_2D, texInfo->texture);
204 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
205 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
207 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
208 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
210 texInfo->size = QSize(width, height);
212 GLuint error = glGetError();
213 if (error != GL_NO_ERROR) {
214 glBindTexture(GL_TEXTURE_2D, 0);
215 glDeleteTextures(1, &texInfo->texture);
216 texInfo->texture = 0;
221 static void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
223 funcs->glDeleteFramebuffers(1, &id);
226 void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
228 int oldWidth = texInfo->size.width();
229 int oldHeight = texInfo->size.height();
230 if (width == oldWidth && height == oldHeight)
233 GLuint oldTexture = texInfo->texture;
234 createTexture(texInfo, width, height);
239 updateTexture(oldTexture, texInfo->texture, texInfo->size);
241 if (useWorkaroundBrokenFBOReadback()) {
242 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, texInfo->image.constBits());
243 texInfo->image = texInfo->image.copy(0, 0, width, height);
244 glDeleteTextures(1, &oldTexture);
251 Q_ASSERT(m_blitProgram);
255 ctx->functions()->glGenFramebuffers(1, &fbo);
256 m_fboGuard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
258 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_fboGuard->id());
261 glGenTextures(1, &tmp_texture);
262 glBindTexture(GL_TEXTURE_2D, tmp_texture);
263 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
264 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
265 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
266 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
267 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
268 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
269 glBindTexture(GL_TEXTURE_2D, 0);
270 ctx->functions()->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
271 GL_TEXTURE_2D, tmp_texture, 0);
273 ctx->functions()->glActiveTexture(GL_TEXTURE0);
274 glBindTexture(GL_TEXTURE_2D, oldTexture);
276 // save current render states
277 GLboolean stencilTestEnabled;
278 GLboolean depthTestEnabled;
279 GLboolean scissorTestEnabled;
280 GLboolean blendEnabled;
283 glGetBooleanv(GL_STENCIL_TEST, &stencilTestEnabled);
284 glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled);
285 glGetBooleanv(GL_SCISSOR_TEST, &scissorTestEnabled);
286 glGetBooleanv(GL_BLEND, &blendEnabled);
287 glGetIntegerv(GL_VIEWPORT, &viewport[0]);
288 glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgram);
290 glDisable(GL_STENCIL_TEST);
291 glDisable(GL_DEPTH_TEST);
292 glDisable(GL_SCISSOR_TEST);
295 glViewport(0, 0, oldWidth, oldHeight);
297 ctx->functions()->glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_blitVertexCoordinateArray);
298 ctx->functions()->glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_blitTextureCoordinateArray);
300 m_blitProgram->bind();
301 m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
302 m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
303 m_blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR));
304 m_blitProgram->setUniformValue("imageTexture", GLuint(0));
306 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
308 glBindTexture(GL_TEXTURE_2D, texInfo->texture);
310 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
312 ctx->functions()->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
314 glDeleteTextures(1, &tmp_texture);
315 glDeleteTextures(1, &oldTexture);
317 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
319 // restore render states
320 if (stencilTestEnabled)
321 glEnable(GL_STENCIL_TEST);
322 if (depthTestEnabled)
323 glEnable(GL_DEPTH_TEST);
324 if (scissorTestEnabled)
325 glEnable(GL_SCISSOR_TEST);
328 glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
329 ctx->functions()->glUseProgram(oldProgram);
331 m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
332 m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
335 bool QSGDefaultDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const
337 static bool set = false;
338 static bool useWorkaround = false;
340 QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(ctx));
341 useWorkaround = ctx_p->workaround_brokenFBOReadBack;
344 return useWorkaround;
347 int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
349 if (!m_maxTextureSize)
350 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
351 return m_maxTextureSize;