07c17a57916197eb8a487a68d9accdbb1314b456
[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 <QtQuick/private/qsgdistancefieldutil_p.h>
46 #include <qopenglfunctions.h>
47 #include <qmath.h>
48
49 QT_BEGIN_NAMESPACE
50
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_fbo(0)
57     , m_blitProgram(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     ctx->functions()->glDeleteFramebuffers(1, &m_fbo);
85     delete m_blitProgram;
86     delete m_areaAllocator;
87 }
88
89 void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
90 {
91     QList<GlyphPosition> glyphPositions;
92     QVector<glyph_t> glyphsToRender;
93
94     for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
95         glyph_t glyphIndex = *it;
96
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);
100
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();
105
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())));
109
110                 m_unusedGlyphs.remove(unusedGlyph);
111                 m_glyphsTexture.remove(unusedGlyph);
112                 removeGlyph(unusedGlyph);
113
114                 alloc = m_areaAllocator->allocate(glyphSize);
115             }
116
117             // Not enough space left for this glyph... skip to the next one
118             if (alloc.isNull())
119                 continue;
120         }
121
122         TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize());
123         alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height());
124         tex->allocatedArea |= alloc;
125
126         GlyphPosition p;
127         p.glyph = glyphIndex;
128         p.position = alloc.topLeft();
129
130         glyphPositions.append(p);
131         glyphsToRender.append(glyphIndex);
132         m_glyphsTexture.insert(glyphIndex, tex);
133     }
134
135     setGlyphsPosition(glyphPositions);
136     markGlyphsToRender(glyphsToRender);
137 }
138
139 void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
140 {
141     QHash<TextureInfo *, QVector<glyph_t> > glyphTextures;
142
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);
148
149         resizeTexture(texInfo, texInfo->allocatedArea.width(), texInfo->allocatedArea.height());
150         glBindTexture(GL_TEXTURE_2D, texInfo->texture);
151
152         glyphTextures[texInfo].append(glyphIndex);
153
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());
158
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();
166             }
167         }
168
169         glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits());
170     }
171
172     QHash<TextureInfo *, QVector<glyph_t> >::const_iterator i;
173     for (i = glyphTextures.constBegin(); i != glyphTextures.constEnd(); ++i) {
174         Texture t;
175         t.textureId = i.key()->texture;
176         t.size = i.key()->size;
177         setGlyphsTexture(i.value(), t);
178     }
179 }
180
181 void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
182 {
183     m_unusedGlyphs -= glyphs;
184 }
185
186 void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
187 {
188     m_unusedGlyphs += glyphs;
189 }
190
191 void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height)
192 {
193     if (useWorkaroundBrokenFBOReadback() && texInfo->image.isNull())
194         texInfo->image = QImage(width, height, QImage::Format_Indexed8);
195
196     while (glGetError() != GL_NO_ERROR) { }
197
198     glGenTextures(1, &texInfo->texture);
199     glBindTexture(GL_TEXTURE_2D, texInfo->texture);
200
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);
206
207     texInfo->size = QSize(width, height);
208
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;
214     }
215
216 }
217
218 void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
219 {
220     int oldWidth = texInfo->size.width();
221     int oldHeight = texInfo->size.height();
222     if (width == oldWidth && height == oldHeight)
223         return;
224
225     GLuint oldTexture = texInfo->texture;
226     createTexture(texInfo, width, height);
227
228     if (!oldTexture)
229         return;
230
231     updateTexture(oldTexture, texInfo->texture, texInfo->size);
232
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);
237         return;
238     }
239
240     if (!m_blitProgram)
241         createBlitProgram();
242
243     Q_ASSERT(m_blitProgram);
244
245     if (!m_fbo)
246         ctx->functions()->glGenFramebuffers(1, &m_fbo);
247     ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
248
249     GLuint tmp_texture;
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);
261
262     ctx->functions()->glActiveTexture(GL_TEXTURE0);
263     glBindTexture(GL_TEXTURE_2D, oldTexture);
264
265     // save current render states
266     GLboolean stencilTestEnabled;
267     GLboolean depthTestEnabled;
268     GLboolean scissorTestEnabled;
269     GLboolean blendEnabled;
270     GLint viewport[4];
271     GLint oldProgram;
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);
278
279     glDisable(GL_STENCIL_TEST);
280     glDisable(GL_DEPTH_TEST);
281     glDisable(GL_SCISSOR_TEST);
282     glDisable(GL_BLEND);
283
284     glViewport(0, 0, oldWidth, oldHeight);
285
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);
288
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));
294
295     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
296
297     glBindTexture(GL_TEXTURE_2D, texInfo->texture);
298
299     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
300
301     ctx->functions()->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
302                                                 GL_RENDERBUFFER, 0);
303     glDeleteTextures(1, &tmp_texture);
304     glDeleteTextures(1, &oldTexture);
305
306     ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
307
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);
315     if (blendEnabled)
316         glEnable(GL_BLEND);
317     glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
318     ctx->functions()->glUseProgram(oldProgram);
319
320     m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
321     m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
322 }
323
324 bool QSGDefaultDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const
325 {
326     static bool set = false;
327     static bool useWorkaround = false;
328     if (!set) {
329         QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(ctx));
330         useWorkaround = ctx_p->workaround_brokenFBOReadBack;
331         set = true;
332     }
333     return useWorkaround;
334 }
335
336 int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
337 {
338     if (!m_maxTextureSize)
339         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
340     return m_maxTextureSize;
341 }
342
343 QT_END_NAMESPACE