1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsgdefaultdistancefieldglyphcache_p.h"
44 #include <QtQuick/private/qsgdistancefieldutil_p.h>
45 #include <qopenglfunctions.h>
49 QHash<QString, QOpenGLMultiGroupSharedResource> QSGDefaultDistanceFieldGlyphCache::m_textures_data;
51 QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData *QSGDefaultDistanceFieldGlyphCache::textureData(QOpenGLContext *c)
53 QString key = QString::fromLatin1("%1_%2_%3_%4")
54 .arg(font().familyName())
55 .arg(font().styleName())
58 return m_textures_data[key].value<QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData>(c);
61 QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
62 : QSGDistanceFieldGlyphCache(man, c, font)
65 m_textureData = textureData(c);
68 void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
70 QList<GlyphPosition> glyphPositions;
71 QVector<glyph_t> glyphsToRender;
73 for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
74 glyph_t glyphIndex = *it;
76 if (cacheIsFull() && m_textureData->unusedGlyphs.isEmpty())
79 m_textureData->unusedGlyphs.remove(glyphIndex);
83 p.position = QPointF(m_textureData->currX, m_textureData->currY);
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());
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);
102 if (p.position.y() < maxTextureSize()) {
103 glyphPositions.append(p);
104 glyphsToRender.append(glyphIndex);
108 setGlyphsPosition(glyphPositions);
109 markGlyphsToRender(glyphsToRender);
112 void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> &glyphs)
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));
120 resizeTexture((requiredWidth), (requiredHeight));
121 glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
123 QVector<glyph_t> glyphTextures;
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);
130 glyphTextures.append(glyphIndex);
132 QImage glyph = it.value();
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();
144 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits());
148 t.textureId = m_textureData->texture;
149 t.size = m_textureData->size;
150 setGlyphsTexture(glyphTextures, t);
153 void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
155 m_textureData->unusedGlyphs -= glyphs;
158 void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
160 m_textureData->unusedGlyphs += glyphs;
163 void QSGDefaultDistanceFieldGlyphCache::createTexture(int width, int height)
165 if (useWorkaroundBrokenFBOReadback() && m_textureData->image.isNull())
166 m_textureData->image = QImage(width, height, QImage::Format_Indexed8);
168 while (glGetError() != GL_NO_ERROR) { }
170 glGenTextures(1, &m_textureData->texture);
171 glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
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);
179 m_textureData->size = QSize(width, height);
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;
190 void QSGDefaultDistanceFieldGlyphCache::resizeTexture(int width, int height)
192 int oldWidth = m_textureData->size.width();
193 int oldHeight = m_textureData->size.height();
194 if (width == oldWidth && height == oldHeight)
197 GLuint oldTexture = m_textureData->texture;
198 createTexture(width, height);
203 updateTexture(oldTexture, m_textureData->texture, m_textureData->size);
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);
212 if (!m_textureData->blitProgram)
213 m_textureData->createBlitProgram();
215 Q_ASSERT(m_textureData->blitProgram);
217 if (!m_textureData->fbo)
218 ctx->functions()->glGenFramebuffers(1, &m_textureData->fbo);
219 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_textureData->fbo);
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);
234 ctx->functions()->glActiveTexture(GL_TEXTURE0);
235 glBindTexture(GL_TEXTURE_2D, oldTexture);
237 // save current render states
238 GLboolean stencilTestEnabled;
239 GLboolean depthTestEnabled;
240 GLboolean scissorTestEnabled;
241 GLboolean blendEnabled;
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);
251 glDisable(GL_STENCIL_TEST);
252 glDisable(GL_DEPTH_TEST);
253 glDisable(GL_SCISSOR_TEST);
256 glViewport(0, 0, oldWidth, oldHeight);
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);
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));
267 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
269 glBindTexture(GL_TEXTURE_2D, m_textureData->texture);
271 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
273 ctx->functions()->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
275 glDeleteTextures(1, &tmp_texture);
276 glDeleteTextures(1, &oldTexture);
278 ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
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);
289 glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
290 ctx->functions()->glUseProgram(oldProgram);
292 m_textureData->blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
293 m_textureData->blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
296 bool QSGDefaultDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const
298 static bool set = false;
299 static bool useWorkaround = false;
301 QOpenGLContextPrivate *ctx_p = static_cast<QOpenGLContextPrivate *>(QOpenGLContextPrivate::get(ctx));
302 useWorkaround = ctx_p->workaround_brokenFBOReadBack;
305 return useWorkaround;
308 int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
310 if (!m_maxTextureSize)
311 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
312 return m_maxTextureSize;