source: trunk/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 11.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtOpenGL module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qtextureglyphcache_gl_p.h"
43#include "qpaintengineex_opengl2_p.h"
44
45#if defined QT_OPENGL_ES_2 && !defined(QT_NO_EGL)
46#include "private/qeglcontext_p.h"
47#endif
48
49QT_BEGIN_NAMESPACE
50
51#ifdef Q_WS_WIN
52extern Q_GUI_EXPORT bool qt_cleartype_enabled;
53#endif
54
55QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix)
56 : QImageTextureGlyphCache(type, matrix)
57 , ctx(0)
58 , m_width(0)
59 , m_height(0)
60 , m_filterMode(Nearest)
61{
62 setContext(context);
63}
64
65void QGLTextureGlyphCache::setContext(QGLContext *context)
66{
67 ctx = context;
68
69 // broken FBO readback is a bug in the SGX 1.3 and 1.4 drivers for the N900 where
70 // copying between FBO's is broken if the texture is either GL_ALPHA or POT. The
71 // workaround is to use a system-memory copy of the glyph cache for this device.
72 // Switching to NPOT and GL_RGBA would both cost a lot more graphics memory and
73 // be slower, so that is not desireable.
74 if (!ctx->d_ptr->workaround_brokenFBOReadBack)
75 glGenFramebuffers(1, &m_fbo);
76
77 connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext*)),
78 SLOT(contextDestroyed(const QGLContext*)));
79}
80
81void QGLTextureGlyphCache::clear()
82{
83 if (ctx) {
84 QGLShareContextScope scope(ctx);
85
86 if (m_width || m_height)
87 glDeleteTextures(1, &m_texture);
88
89 m_texture = 0;
90 m_width = 0;
91 m_height = 0;
92 m_w = 0;
93 m_h = 0;
94 m_cx = 0;
95 m_cy = 0;
96 m_currentRowHeight = 0;
97 coords.clear();
98 }
99
100}
101
102QGLTextureGlyphCache::~QGLTextureGlyphCache()
103{
104 if (ctx) {
105 QGLShareContextScope scope(ctx);
106
107 if (!ctx->d_ptr->workaround_brokenFBOReadBack)
108 glDeleteFramebuffers(1, &m_fbo);
109 }
110
111 clear();
112}
113
114void QGLTextureGlyphCache::createTextureData(int width, int height)
115{
116 // create in QImageTextureGlyphCache baseclass is meant to be called
117 // only to create the initial image and does not preserve the content,
118 // so we don't call when this function is called from resize.
119 if (ctx->d_ptr->workaround_brokenFBOReadBack && image().isNull())
120 QImageTextureGlyphCache::createTextureData(width, height);
121
122 // Make the lower glyph texture size 16 x 16.
123 if (width < 16)
124 width = 16;
125 if (height < 16)
126 height = 16;
127
128 glGenTextures(1, &m_texture);
129 glBindTexture(GL_TEXTURE_2D, m_texture);
130
131 m_width = width;
132 m_height = height;
133
134 if (m_type == QFontEngineGlyphCache::Raster_RGBMask) {
135 QVarLengthArray<uchar> data(width * height * 4);
136 for (int i = 0; i < data.size(); ++i)
137 data[i] = 0;
138 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);
139 } else {
140 QVarLengthArray<uchar> data(width * height);
141 for (int i = 0; i < data.size(); ++i)
142 data[i] = 0;
143 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &data[0]);
144 }
145
146 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
147 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
148 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
149 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
150 m_filterMode = Nearest;
151}
152
153void QGLTextureGlyphCache::resizeTextureData(int width, int height)
154{
155 int oldWidth = m_width;
156 int oldHeight = m_height;
157
158 // Make the lower glyph texture size 16 x 16.
159 if (width < 16)
160 width = 16;
161 if (height < 16)
162 height = 16;
163
164 GLuint oldTexture = m_texture;
165 createTextureData(width, height);
166
167 if (ctx->d_ptr->workaround_brokenFBOReadBack) {
168 QImageTextureGlyphCache::resizeTextureData(width, height);
169 Q_ASSERT(image().depth() == 8);
170 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, image().constBits());
171 glDeleteTextures(1, &oldTexture);
172 return;
173 }
174
175 // ### the QTextureGlyphCache API needs to be reworked to allow
176 // ### resizeTextureData to fail
177
178 glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fbo);
179
180 GLuint tmp_texture;
181 glGenTextures(1, &tmp_texture);
182 glBindTexture(GL_TEXTURE_2D, tmp_texture);
183 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0,
184 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
185 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
186 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
187 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
188 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
189 m_filterMode = Nearest;
190 glBindTexture(GL_TEXTURE_2D, 0);
191 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
192 GL_TEXTURE_2D, tmp_texture, 0);
193
194 glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
195 glBindTexture(GL_TEXTURE_2D, oldTexture);
196
197 pex->transferMode(BrushDrawingMode);
198
199 glDisable(GL_STENCIL_TEST);
200 glDisable(GL_DEPTH_TEST);
201 glDisable(GL_SCISSOR_TEST);
202 glDisable(GL_BLEND);
203
204 glViewport(0, 0, oldWidth, oldHeight);
205
206 GLfloat* vertexCoordinateArray = pex->staticVertexCoordinateArray;
207 vertexCoordinateArray[0] = -1.0f;
208 vertexCoordinateArray[1] = -1.0f;
209 vertexCoordinateArray[2] = 1.0f;
210 vertexCoordinateArray[3] = -1.0f;
211 vertexCoordinateArray[4] = 1.0f;
212 vertexCoordinateArray[5] = 1.0f;
213 vertexCoordinateArray[6] = -1.0f;
214 vertexCoordinateArray[7] = 1.0f;
215
216 GLfloat* textureCoordinateArray = pex->staticTextureCoordinateArray;
217 textureCoordinateArray[0] = 0.0f;
218 textureCoordinateArray[1] = 0.0f;
219 textureCoordinateArray[2] = 1.0f;
220 textureCoordinateArray[3] = 0.0f;
221 textureCoordinateArray[4] = 1.0f;
222 textureCoordinateArray[5] = 1.0f;
223 textureCoordinateArray[6] = 0.0f;
224 textureCoordinateArray[7] = 1.0f;
225
226 pex->setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, vertexCoordinateArray);
227 pex->setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, textureCoordinateArray);
228
229 pex->shaderManager->useBlitProgram();
230 pex->shaderManager->blitProgram()->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT);
231
232 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
233
234 glBindTexture(GL_TEXTURE_2D, m_texture);
235
236 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight);
237
238 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
239 GL_RENDERBUFFER_EXT, 0);
240 glDeleteTextures(1, &tmp_texture);
241 glDeleteTextures(1, &oldTexture);
242
243 glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
244
245 glViewport(0, 0, pex->width, pex->height);
246 pex->updateClipScissorTest();
247}
248
249void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph)
250{
251 if (ctx->d_ptr->workaround_brokenFBOReadBack) {
252 QImageTextureGlyphCache::fillTexture(c, glyph);
253
254 glBindTexture(GL_TEXTURE_2D, m_texture);
255 const QImage &texture = image();
256 const uchar *bits = texture.constBits();
257 bits += c.y * texture.bytesPerLine() + c.x;
258 for (int i=0; i<c.h; ++i) {
259 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits);
260 bits += texture.bytesPerLine();
261 }
262
263 return;
264 }
265
266 QImage mask = textureMapForGlyph(glyph);
267 const int maskWidth = mask.width();
268 const int maskHeight = mask.height();
269
270 if (mask.format() == QImage::Format_Mono) {
271 mask = mask.convertToFormat(QImage::Format_Indexed8);
272 for (int y = 0; y < maskHeight; ++y) {
273 uchar *src = (uchar *) mask.scanLine(y);
274 for (int x = 0; x < maskWidth; ++x)
275 src[x] = -src[x]; // convert 0 and 1 into 0 and 255
276 }
277 } else if (mask.format() == QImage::Format_RGB32) {
278 // Make the alpha component equal to the average of the RGB values.
279 // This is needed when drawing sub-pixel antialiased text on translucent targets.
280 for (int y = 0; y < maskHeight; ++y) {
281 quint32 *src = (quint32 *) mask.scanLine(y);
282 for (int x = 0; x < maskWidth; ++x) {
283 uchar r = src[x] >> 16;
284 uchar g = src[x] >> 8;
285 uchar b = src[x];
286 quint32 avg = (quint32(r) + quint32(g) + quint32(b) + 1) / 3; // "+1" for rounding.
287 src[x] = (src[x] & 0x00ffffff) | (avg << 24);
288 }
289 }
290 }
291
292 glBindTexture(GL_TEXTURE_2D, m_texture);
293 if (mask.format() == QImage::Format_RGB32) {
294 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_BGRA, GL_UNSIGNED_BYTE, mask.bits());
295 } else {
296 // glTexSubImage2D() might cause some garbage to appear in the texture if the mask width is
297 // not a multiple of four bytes. The bug appeared on a computer with 32-bit Windows Vista
298 // and nVidia GeForce 8500GT. GL_UNPACK_ALIGNMENT is set to four bytes, 'mask' has a
299 // multiple of four bytes per line, and most of the glyph shows up correctly in the
300 // texture, which makes me think that this is a driver bug.
301 // One workaround is to make sure the mask width is a multiple of four bytes, for instance
302 // by converting it to a format with four bytes per pixel. Another is to copy one line at a
303 // time.
304
305 for (int i = 0; i < maskHeight; ++i)
306 glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, maskWidth, 1, GL_ALPHA, GL_UNSIGNED_BYTE, mask.scanLine(i));
307 }
308}
309
310int QGLTextureGlyphCache::glyphPadding() const
311{
312 return 1;
313}
314
315int QGLTextureGlyphCache::maxTextureWidth() const
316{
317 return ctx->d_ptr->maxTextureSize();
318}
319
320int QGLTextureGlyphCache::maxTextureHeight() const
321{
322 if (ctx->d_ptr->workaround_brokenTexSubImage)
323 return qMin(1024, ctx->d_ptr->maxTextureSize());
324 else
325 return ctx->d_ptr->maxTextureSize();
326}
327QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.