Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgdefaultdistancefieldglyphcache.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgdefaultdistancefieldglyphcache_p.h"
43
44 #include <QtQuick/private/qsgdistancefieldutil_p.h>
45 #include <qopenglfunctions.h>
46
47 QT_BEGIN_NAMESPACE
48
49 QHash<QString, QOpenGLMultiGroupSharedResource> QSGDefaultDistanceFieldGlyphCache::m_textures_data;
50
51 QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData *QSGDefaultDistanceFieldGlyphCache::textureData(QOpenGLContext *c)
52 {
53     QString key = QString::fromLatin1("%1_%2_%3_%4")
54             .arg(font().familyName())
55             .arg(font().styleName())
56             .arg(font().weight())
57             .arg(font().style());
58     return m_textures_data[key].value<QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData>(c);
59 }
60
61 QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
62     : QSGDistanceFieldGlyphCache(man, c, font)
63     , m_maxTextureSize(0)
64 {
65     m_textureData = textureData(c);
66 }
67
68 void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
69 {
70     QList<GlyphPosition> glyphPositions;
71     QVector<glyph_t> glyphsToRender;
72
73     for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
74         glyph_t glyphIndex = *it;
75
76         if (cacheIsFull() && m_textureData->unusedGlyphs.isEmpty())
77             continue;
78
79         m_textureData->unusedGlyphs.remove(glyphIndex);
80
81         GlyphPosition p;
82         p.glyph = glyphIndex;
83         p.position = QPointF(m_textureData->currX, m_textureData->currY);
84
85         if (!cacheIsFull()) {
86             m_textureData->currX += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution());
87             if (m_textureData->currX >= maxTextureSize()) {
88                 m_textureData->currX = 0;
89                 m_textureData->currY += QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution());
90             }
91         } else {
92             // Recycle glyphs
93             if (!m_textureData->unusedGlyphs.isEmpty()) {
94                 glyph_t unusedGlyph = *m_textureData->unusedGlyphs.constBegin();
95                 TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
96                 p.position = QPointF(unusedCoord.x, unusedCoord.y);
97                 m_textureData->unusedGlyphs.remove(unusedGlyph);
98                 removeGlyph(unusedGlyph);
99             }
100         }
101
102         if (p.position.y() < maxTextureSize()) {
103             glyphPositions.append(p);
104             glyphsToRender.append(glyphIndex);
105         }
106     }
107
108     setGlyphsPosition(glyphPositions);
109     markGlyphsToRender(glyphsToRender);
110 }
111
112 void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
113 {
114     int requiredWidth = maxTextureSize();
115     int rows = 128 / (requiredWidth / QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution())); // Enough rows to fill the latin1 set by default..
116     int requiredHeight = qMin(maxTextureSize(),
117                               qMax(m_textureData->currY + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()),
118                                    QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()) * rows));
119
120     resizeTexture((requiredWidth), (requiredHeight));
121     glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
122
123     QVector<glyph_t> glyphTextures;
124
125     QHash<glyph_t, QImage>::const_iterator it;
126     for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) {
127         glyph_t glyphIndex = it.key();
128         TexCoord c = glyphTexCoord(glyphIndex);
129
130         glyphTextures.append(glyphIndex);
131
132         QImage glyph = it.value();
133
134         if (useWorkaroundBrokenFBOReadback()) {
135             uchar *inBits = glyph.scanLine(0);
136             uchar *outBits = m_textureData->image.scanLine(int(c.y)) + int(c.x);
137             for (int y = 0; y < glyph.height(); ++y) {
138                 qMemCopy(outBits, inBits, glyph.width());
139                 inBits += glyph.bytesPerLine();
140                 outBits += m_textureData->image.bytesPerLine();
141             }
142         }
143
144         glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits());
145     }
146
147     Texture t;
148     t.textureId = m_textureData->texture;
149     t.size = m_textureData->size;
150     setGlyphsTexture(glyphTextures, t);
151 }
152
153 void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
154 {
155     m_textureData->unusedGlyphs -= glyphs;
156 }
157
158 void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
159 {
160     m_textureData->unusedGlyphs += glyphs;
161 }
162
163 void QSGDefaultDistanceFieldGlyphCache::createTexture(int width, int height)
164 {
165     if (useWorkaroundBrokenFBOReadback() && m_textureData->image.isNull())
166         m_textureData->image = QImage(width, height, QImage::Format_Indexed8);
167
168     while (glGetError() != GL_NO_ERROR) { }
169
170     glGenTextures(1, &m_textureData->texture);
171     glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
172
173     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
174     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
175     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
176     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
177     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
178
179     m_textureData->size = QSize(width, height);
180
181     GLuint error = glGetError();
182     if (error != GL_NO_ERROR) {
183         glBindTexture(GL_TEXTURE_2D, 0);
184         glDeleteTextures(1, &m_textureData->texture);
185         m_textureData->texture = 0;
186     }
187
188 }
189
190 void QSGDefaultDistanceFieldGlyphCache::resizeTexture(int width, int height)
191 {
192     int oldWidth = m_textureData->size.width();
193     int oldHeight = m_textureData->size.height();
194     if (width == oldWidth && height == oldHeight)
195         return;
196
197     GLuint oldTexture = m_textureData->texture;
198     createTexture(width, height);
199
200     if (!oldTexture)
201         return;
202
203     updateTexture(oldTexture, m_textureData->texture, m_textureData->size);
204
205     if (useWorkaroundBrokenFBOReadback()) {
206         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, oldWidth, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, m_textureData->image.constBits());
207         m_textureData->image = m_textureData->image.copy(0, 0, width, height);
208         glDeleteTextures(1, &oldTexture);
209         return;
210     }
211
212     if (!m_textureData->blitProgram)
213         m_textureData->createBlitProgram();
214
215     Q_ASSERT(m_textureData->blitProgram);
216
217     if (!m_textureData->fbo)
218         ctx->functions()->glGenFramebuffers(1, &m_textureData->fbo);
219     ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_textureData->fbo);
220
221     GLuint tmp_texture;
222     glGenTextures(1, &tmp_texture);
223     glBindTexture(GL_TEXTURE_2D, tmp_texture);
224     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
225                  GL_RGBA, GL_UNSIGNED_BYTE, NULL);
226     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
227     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
228     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
229     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
230     glBindTexture(GL_TEXTURE_2D, 0);
231     ctx->functions()->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
232                                              GL_TEXTURE_2D, tmp_texture, 0);
233
234     ctx->functions()->glActiveTexture(GL_TEXTURE0);
235     glBindTexture(GL_TEXTURE_2D, oldTexture);
236
237     // save current render states
238     GLboolean stencilTestEnabled;
239     GLboolean depthTestEnabled;
240     GLboolean scissorTestEnabled;
241     GLboolean blendEnabled;
242     GLint viewport[4];
243     GLint oldProgram;
244     glGetBooleanv(GL_STENCIL_TEST, &stencilTestEnabled);
245     glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled);
246     glGetBooleanv(GL_SCISSOR_TEST, &scissorTestEnabled);
247     glGetBooleanv(GL_BLEND, &blendEnabled);
248     glGetIntegerv(GL_VIEWPORT, &viewport[0]);
249     glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgram);
250
251     glDisable(GL_STENCIL_TEST);
252     glDisable(GL_DEPTH_TEST);
253     glDisable(GL_SCISSOR_TEST);
254     glDisable(GL_BLEND);
255
256     glViewport(0, 0, oldWidth, oldHeight);
257
258     ctx->functions()->glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureData->blitVertexCoordinateArray);
259     ctx->functions()->glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureData->blitTextureCoordinateArray);
260
261     m_textureData->blitProgram->bind();
262     m_textureData->blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
263     m_textureData->blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
264     m_textureData->blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR));
265     m_textureData->blitProgram->setUniformValue("imageTexture", GLuint(0));
266
267     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
268
269     glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
270
271     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
272
273     ctx->functions()->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
274                                                 GL_RENDERBUFFER, 0);
275     glDeleteTextures(1, &tmp_texture);
276     glDeleteTextures(1, &oldTexture);
277
278     ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
279
280     // restore render states
281     if (stencilTestEnabled)
282         glEnable(GL_STENCIL_TEST);
283     if (depthTestEnabled)
284         glEnable(GL_DEPTH_TEST);
285     if (scissorTestEnabled)
286         glEnable(GL_SCISSOR_TEST);
287     if (blendEnabled)
288         glEnable(GL_BLEND);
289     glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
290     ctx->functions()->glUseProgram(oldProgram);
291
292     m_textureData->blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
293     m_textureData->blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
294 }
295
296 bool QSGDefaultDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const
297 {
298     static bool set = false;
299     static bool useWorkaround = false;
300     if (!set) {
301         QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(ctx));
302         useWorkaround = ctx_p->workaround_brokenFBOReadBack;
303         set = true;
304     }
305     return useWorkaround;
306 }
307
308 int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
309 {
310     if (!m_maxTextureSize)
311         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
312     return m_maxTextureSize;
313 }
314
315 QT_END_NAMESPACE