Fix potential crash when using text and more than 1 QQuickView
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgdefaultdistancefieldglyphcache.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
24 **
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.
28 **
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.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgdefaultdistancefieldglyphcache_p.h"
43
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>
48 #include <qmath.h>
49
50 QT_BEGIN_NAMESPACE
51
52 QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
53     : QSGDistanceFieldGlyphCache(man, c, font)
54     , m_maxTextureSize(0)
55     , m_maxTextureCount(3)
56     , m_blitProgram(0)
57     , m_fboGuard(0)
58 {
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;
67
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;
76
77     m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
78 }
79
80 QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache()
81 {
82     for (int i = 0; i < m_textures.count(); ++i)
83         glDeleteTextures(1, &m_textures[i].texture);
84
85     if (m_fboGuard != 0)
86         m_fboGuard->free();
87
88     delete m_blitProgram;
89     delete m_areaAllocator;
90 }
91
92 void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
93 {
94     QList<GlyphPosition> glyphPositions;
95     QVector<glyph_t> glyphsToRender;
96
97     for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
98         glyph_t glyphIndex = *it;
99
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);
103
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();
108
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())));
112
113                 m_unusedGlyphs.remove(unusedGlyph);
114                 m_glyphsTexture.remove(unusedGlyph);
115                 removeGlyph(unusedGlyph);
116
117                 alloc = m_areaAllocator->allocate(glyphSize);
118             }
119
120             // Not enough space left for this glyph... skip to the next one
121             if (alloc.isNull())
122                 continue;
123         }
124
125         TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize());
126         alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height());
127         tex->allocatedArea |= alloc;
128
129         GlyphPosition p;
130         p.glyph = glyphIndex;
131         p.position = alloc.topLeft();
132
133         glyphPositions.append(p);
134         glyphsToRender.append(glyphIndex);
135         m_glyphsTexture.insert(glyphIndex, tex);
136     }
137
138     setGlyphsPosition(glyphPositions);
139     markGlyphsToRender(glyphsToRender);
140 }
141
142 void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
143 {
144     QHash<TextureInfo *, QVector<glyph_t> > glyphTextures;
145
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);
151
152         resizeTexture(texInfo, texInfo->allocatedArea.width(), texInfo->allocatedArea.height());
153         glBindTexture(GL_TEXTURE_2D, texInfo->texture);
154
155         glyphTextures[texInfo].append(glyphIndex);
156
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());
161
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();
169             }
170         }
171
172         glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits());
173     }
174
175     QHash<TextureInfo *, QVector<glyph_t> >::const_iterator i;
176     for (i = glyphTextures.constBegin(); i != glyphTextures.constEnd(); ++i) {
177         Texture t;
178         t.textureId = i.key()->texture;
179         t.size = i.key()->size;
180         setGlyphsTexture(i.value(), t);
181     }
182 }
183
184 void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
185 {
186     m_unusedGlyphs -= glyphs;
187 }
188
189 void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
190 {
191     m_unusedGlyphs += glyphs;
192 }
193
194 void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height)
195 {
196     if (useWorkaroundBrokenFBOReadback() && texInfo->image.isNull())
197         texInfo->image = QImage(width, height, QImage::Format_Indexed8);
198
199     while (glGetError() != GL_NO_ERROR) { }
200
201     glGenTextures(1, &texInfo->texture);
202     glBindTexture(GL_TEXTURE_2D, texInfo->texture);
203
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);
209
210     texInfo->size = QSize(width, height);
211
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;
217     }
218
219 }
220
221 static void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
222 {
223     funcs->glDeleteFramebuffers(1, &id);
224 }
225
226 void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
227 {
228     int oldWidth = texInfo->size.width();
229     int oldHeight = texInfo->size.height();
230     if (width == oldWidth && height == oldHeight)
231         return;
232
233     GLuint oldTexture = texInfo->texture;
234     createTexture(texInfo, width, height);
235
236     if (!oldTexture)
237         return;
238
239     updateTexture(oldTexture, texInfo->texture, texInfo->size);
240
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);
245         return;
246     }
247
248     if (!m_blitProgram)
249         createBlitProgram();
250
251     Q_ASSERT(m_blitProgram);
252
253     if (!m_fboGuard) {
254         GLuint fbo;
255         ctx->functions()->glGenFramebuffers(1, &fbo);
256         m_fboGuard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc);
257     }
258     ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_fboGuard->id());
259
260     GLuint tmp_texture;
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);
272
273     ctx->functions()->glActiveTexture(GL_TEXTURE0);
274     glBindTexture(GL_TEXTURE_2D, oldTexture);
275
276     // save current render states
277     GLboolean stencilTestEnabled;
278     GLboolean depthTestEnabled;
279     GLboolean scissorTestEnabled;
280     GLboolean blendEnabled;
281     GLint viewport[4];
282     GLint oldProgram;
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);
289
290     glDisable(GL_STENCIL_TEST);
291     glDisable(GL_DEPTH_TEST);
292     glDisable(GL_SCISSOR_TEST);
293     glDisable(GL_BLEND);
294
295     glViewport(0, 0, oldWidth, oldHeight);
296
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);
299
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));
305
306     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
307
308     glBindTexture(GL_TEXTURE_2D, texInfo->texture);
309
310     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
311
312     ctx->functions()->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
313                                                 GL_RENDERBUFFER, 0);
314     glDeleteTextures(1, &tmp_texture);
315     glDeleteTextures(1, &oldTexture);
316
317     ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
318
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);
326     if (blendEnabled)
327         glEnable(GL_BLEND);
328     glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
329     ctx->functions()->glUseProgram(oldProgram);
330
331     m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
332     m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
333 }
334
335 bool QSGDefaultDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const
336 {
337     static bool set = false;
338     static bool useWorkaround = false;
339     if (!set) {
340         QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(ctx));
341         useWorkaround = ctx_p->workaround_brokenFBOReadBack;
342         set = true;
343     }
344     return useWorkaround;
345 }
346
347 int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
348 {
349     if (!m_maxTextureSize)
350         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
351     return m_maxTextureSize;
352 }
353
354 QT_END_NAMESPACE