3a3abb19d43ed1b93bd85e32c924c4a82c91761f
[profile/ivi/qtbase.git] / src / opengl / gl2paintengineex / qtextureglyphcache_gl.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtOpenGL module of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qtextureglyphcache_gl_p.h"
43 #include "qpaintengineex_opengl2_p.h"
44 #include "private/qglengineshadersource_p.h"
45
46 QT_BEGIN_NAMESPACE
47
48
49 QBasicAtomicInt qgltextureglyphcache_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1);
50
51 QGLTextureGlyphCache::QGLTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix)
52     : QImageTextureGlyphCache(type, matrix)
53     , pex(0)
54     , m_blitProgram(0)
55     , m_filterMode(Nearest)
56     , m_serialNumber(qgltextureglyphcache_serial_number.fetchAndAddRelaxed(1))
57 {
58 #ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
59     qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, QOpenGLContext::currentContext());
60 #endif
61     m_vertexCoordinateArray[0] = -1.0f;
62     m_vertexCoordinateArray[1] = -1.0f;
63     m_vertexCoordinateArray[2] =  1.0f;
64     m_vertexCoordinateArray[3] = -1.0f;
65     m_vertexCoordinateArray[4] =  1.0f;
66     m_vertexCoordinateArray[5] =  1.0f;
67     m_vertexCoordinateArray[6] = -1.0f;
68     m_vertexCoordinateArray[7] =  1.0f;
69
70     m_textureCoordinateArray[0] = 0.0f;
71     m_textureCoordinateArray[1] = 0.0f;
72     m_textureCoordinateArray[2] = 1.0f;
73     m_textureCoordinateArray[3] = 0.0f;
74     m_textureCoordinateArray[4] = 1.0f;
75     m_textureCoordinateArray[5] = 1.0f;
76     m_textureCoordinateArray[6] = 0.0f;
77     m_textureCoordinateArray[7] = 1.0f;
78 }
79
80 QGLTextureGlyphCache::~QGLTextureGlyphCache()
81 {
82 #ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG
83     qDebug(" -> ~QGLTextureGlyphCache() %p.", this);
84 #endif
85     delete m_blitProgram;
86 }
87
88 void QGLTextureGlyphCache::createTextureData(int width, int height)
89 {
90     QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
91     if (ctx == 0) {
92         qWarning("QGLTextureGlyphCache::createTextureData: Called with no context");
93         return;
94     }
95
96     // create in QImageTextureGlyphCache baseclass is meant to be called
97     // only to create the initial image and does not preserve the content,
98     // so we don't call when this function is called from resize.
99     if (ctx->d_ptr->workaround_brokenFBOReadBack && image().isNull())
100         QImageTextureGlyphCache::createTextureData(width, height);
101
102     // Make the lower glyph texture size 16 x 16.
103     if (width < 16)
104         width = 16;
105     if (height < 16)
106         height = 16;
107
108     if (m_textureResource && !m_textureResource->m_texture)
109         delete m_textureResource;
110
111     if (!m_textureResource)
112         m_textureResource = new QGLGlyphTexture(ctx);
113
114     glGenTextures(1, &m_textureResource->m_texture);
115     glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
116
117     m_textureResource->m_width = width;
118     m_textureResource->m_height = height;
119
120     if (m_type == QFontEngineGlyphCache::Raster_RGBMask) {
121         QVarLengthArray<uchar> data(width * height * 4);
122         for (int i = 0; i < data.size(); ++i)
123             data[i] = 0;
124         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);
125     } else {
126         QVarLengthArray<uchar> data(width * height);
127         for (int i = 0; i < data.size(); ++i)
128             data[i] = 0;
129         glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &data[0]);
130     }
131
132     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
133     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
134     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
135     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
136     m_filterMode = Nearest;
137 }
138
139 void QGLTextureGlyphCache::resizeTextureData(int width, int height)
140 {
141     QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
142     if (ctx == 0) {
143         qWarning("QGLTextureGlyphCache::resizeTextureData: Called with no context");
144         return;
145     }
146
147     int oldWidth = m_textureResource->m_width;
148     int oldHeight = m_textureResource->m_height;
149
150     // Make the lower glyph texture size 16 x 16.
151     if (width < 16)
152         width = 16;
153     if (height < 16)
154         height = 16;
155
156     GLuint oldTexture = m_textureResource->m_texture;
157     createTextureData(width, height);
158
159     if (ctx->d_ptr->workaround_brokenFBOReadBack) {
160         QImageTextureGlyphCache::resizeTextureData(width, height);
161         Q_ASSERT(image().depth() == 8);
162         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, image().constBits());
163         glDeleteTextures(1, &oldTexture);
164         return;
165     }
166
167     // ### the QTextureGlyphCache API needs to be reworked to allow
168     // ### resizeTextureData to fail
169
170     glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_textureResource->m_fbo);
171
172     GLuint tmp_texture;
173     glGenTextures(1, &tmp_texture);
174     glBindTexture(GL_TEXTURE_2D, tmp_texture);
175     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
176                  GL_RGBA, GL_UNSIGNED_BYTE, NULL);
177     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
178     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
179     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
180     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
181     m_filterMode = Nearest;
182     glBindTexture(GL_TEXTURE_2D, 0);
183     glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
184                            GL_TEXTURE_2D, tmp_texture, 0);
185
186     glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
187     glBindTexture(GL_TEXTURE_2D, oldTexture);
188
189     if (pex != 0)
190         pex->transferMode(BrushDrawingMode);
191
192     glDisable(GL_STENCIL_TEST);
193     glDisable(GL_DEPTH_TEST);
194     glDisable(GL_SCISSOR_TEST);
195     glDisable(GL_BLEND);
196
197     glViewport(0, 0, oldWidth, oldHeight);
198
199     QGLShaderProgram *blitProgram = 0;
200     if (pex == 0) {
201         if (m_blitProgram == 0) {
202             m_blitProgram = new QGLShaderProgram(ctx);
203
204             {
205                 QString source;
206                 source.append(QLatin1String(qglslMainWithTexCoordsVertexShader));
207                 source.append(QLatin1String(qglslUntransformedPositionVertexShader));
208
209                 QGLShader *vertexShader = new QGLShader(QGLShader::Vertex, m_blitProgram);
210                 vertexShader->compileSourceCode(source);
211
212                 m_blitProgram->addShader(vertexShader);
213             }
214
215             {
216                 QString source;
217                 source.append(QLatin1String(qglslMainFragmentShader));
218                 source.append(QLatin1String(qglslImageSrcFragmentShader));
219
220                 QGLShader *fragmentShader = new QGLShader(QGLShader::Fragment, m_blitProgram);
221                 fragmentShader->compileSourceCode(source);
222
223                 m_blitProgram->addShader(fragmentShader);
224             }
225
226             m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
227             m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
228
229             m_blitProgram->link();
230         }
231
232         glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_vertexCoordinateArray);
233         glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureCoordinateArray);
234
235         m_blitProgram->bind();
236         m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR));
237         m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR));
238         m_blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR));
239
240         blitProgram = m_blitProgram;
241
242     } else {
243         pex->setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray);
244         pex->setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray);
245
246         pex->shaderManager->useBlitProgram();
247         blitProgram = pex->shaderManager->blitProgram();
248     }
249
250     blitProgram->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT);
251
252     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
253
254     glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
255
256     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
257
258     glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
259                               GL_RENDERBUFFER_EXT, 0);
260     glDeleteTextures(1, &tmp_texture);
261     glDeleteTextures(1, &oldTexture);
262
263     glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
264
265     if (pex != 0) {
266         glViewport(0, 0, pex->width, pex->height);
267         pex->updateClipScissorTest();
268     }
269 }
270
271 void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
272 {
273     QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
274     if (ctx == 0) {
275         qWarning("QGLTextureGlyphCache::fillTexture: Called with no context");
276         return;
277     }
278
279     if (ctx->d_ptr->workaround_brokenFBOReadBack) {
280         QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);
281
282         glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
283         const QImage &texture = image();
284         const uchar *bits = texture.constBits();
285         bits += c.y * texture.bytesPerLine() + c.x;
286         for (int i=0; i<c.h; ++i) {
287             glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits);
288             bits += texture.bytesPerLine();
289         }
290         return;
291     }
292
293     QImage mask = textureMapForGlyph(glyph, subPixelPosition);
294     const int maskWidth = mask.width();
295     const int maskHeight = mask.height();
296
297     if (mask.format() == QImage::Format_Mono) {
298         mask = mask.convertToFormat(QImage::Format_Indexed8);
299         for (int y = 0; y < maskHeight; ++y) {
300             uchar *src = (uchar *) mask.scanLine(y);
301             for (int x = 0; x < maskWidth; ++x)
302                 src[x] = -src[x]; // convert 0 and 1 into 0 and 255
303         }
304     } else if (mask.format() == QImage::Format_RGB32) {
305         // Make the alpha component equal to the average of the RGB values.
306         // This is needed when drawing sub-pixel antialiased text on translucent targets.
307         for (int y = 0; y < maskHeight; ++y) {
308             quint32 *src = (quint32 *) mask.scanLine(y);
309             for (int x = 0; x < maskWidth; ++x) {
310                 uchar r = src[x] >> 16;
311                 uchar g = src[x] >> 8;
312                 uchar b = src[x];
313                 quint32 avg = (quint32(r) + quint32(g) + quint32(b) + 1) / 3; // "+1" for rounding.
314                 src[x] = (src[x] & 0x00ffffff) | (avg << 24);
315             }
316         }
317     }
318
319     glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture);
320     if (mask.format() == QImage::Format_RGB32) {
321         glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA, GL_UNSIGNED_BYTE, mask.bits());
322     } else {
323         // glTexSubImage2D() might cause some garbage to appear in the texture if the mask width is
324         // not a multiple of four bytes. The bug appeared on a computer with 32-bit Windows Vista
325         // and nVidia GeForce 8500GT. GL_UNPACK_ALIGNMENT is set to four bytes, 'mask' has a
326         // multiple of four bytes per line, and most of the glyph shows up correctly in the
327         // texture, which makes me think that this is a driver bug.
328         // One workaround is to make sure the mask width is a multiple of four bytes, for instance
329         // by converting it to a format with four bytes per pixel. Another is to copy one line at a
330         // time.
331
332         if (!ctx->d_ptr->workaround_brokenAlphaTexSubImage_init) {
333             // don't know which driver versions exhibit this bug, so be conservative for now
334             const QByteArray versionString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
335             ctx->d_ptr->workaround_brokenAlphaTexSubImage = versionString.indexOf("NVIDIA") >= 0;
336             ctx->d_ptr->workaround_brokenAlphaTexSubImage_init = true;
337         }
338
339         if (ctx->d_ptr->workaround_brokenAlphaTexSubImage) {
340             for (int i = 0; i < maskHeight; ++i)
341                 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, maskWidth, 1, GL_ALPHA, GL_UNSIGNED_BYTE, mask.scanLine(i));
342         } else {
343             glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_ALPHA, GL_UNSIGNED_BYTE, mask.bits());
344         }
345     }
346 }
347
348 int QGLTextureGlyphCache::glyphPadding() const
349 {
350     return 1;
351 }
352
353 int QGLTextureGlyphCache::maxTextureWidth() const
354 {
355     QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
356     if (ctx == 0)
357         return QImageTextureGlyphCache::maxTextureWidth();
358     else
359         return ctx->d_ptr->maxTextureSize();
360 }
361
362 int QGLTextureGlyphCache::maxTextureHeight() const
363 {
364     QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
365     if (ctx == 0)
366         return QImageTextureGlyphCache::maxTextureHeight();
367
368     if (ctx->d_ptr->workaround_brokenTexSubImage)
369         return qMin(1024, ctx->d_ptr->maxTextureSize());
370     else
371         return ctx->d_ptr->maxTextureSize();
372 }
373
374 void QGLTextureGlyphCache::clear()
375 {
376     m_textureResource->free();
377     m_textureResource = 0;
378
379     m_w = 0;
380     m_h = 0;
381     m_cx = 0;
382     m_cy = 0;
383     m_currentRowHeight = 0;
384     coords.clear();
385 }
386
387 QT_END_NAMESPACE