source: trunk/src/opengl/qpixmapdata_gl.cpp@ 604

Last change on this file since 604 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 20.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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 "qpixmap.h"
43#include "qglframebufferobject.h"
44
45#include <private/qpaintengine_raster_p.h>
46
47#include "qpixmapdata_gl_p.h"
48
49#include <private/qgl_p.h>
50#include <private/qdrawhelper_p.h>
51#include <private/qimage_p.h>
52
53#include <private/qpaintengineex_opengl2_p.h>
54
55#include <qdesktopwidget.h>
56#include <qfile.h>
57#include <qimagereader.h>
58
59QT_BEGIN_NAMESPACE
60
61extern QGLWidget* qt_gl_share_widget();
62
63/*!
64 \class QGLFramebufferObjectPool
65 \since 4.6
66
67 \brief The QGLFramebufferObject class provides a pool of framebuffer
68 objects for offscreen rendering purposes.
69
70 When requesting an FBO of a given size and format, an FBO of the same
71 format and a size at least as big as the requested size will be returned.
72
73 \internal
74*/
75
76static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo)
77{
78 return qAbs(size.width() * size.height() - fbo->width() * fbo->height());
79}
80
81extern int qt_next_power_of_two(int v);
82
83static inline QSize maybeRoundToNextPowerOfTwo(const QSize &sz)
84{
85#ifdef QT_OPENGL_ES_2
86 QSize rounded(qt_next_power_of_two(sz.width()), qt_next_power_of_two(sz.height()));
87 if (rounded.width() * rounded.height() < 1.20 * sz.width() * sz.height())
88 return rounded;
89#endif
90 return sz;
91}
92
93
94QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat, bool strictSize)
95{
96 QGLFramebufferObject *chosen = 0;
97 QGLFramebufferObject *candidate = 0;
98 for (int i = 0; !chosen && i < m_fbos.size(); ++i) {
99 QGLFramebufferObject *fbo = m_fbos.at(i);
100
101 if (strictSize) {
102 if (fbo->size() == requestSize && fbo->format() == requestFormat) {
103 chosen = fbo;
104 break;
105 } else {
106 continue;
107 }
108 }
109
110 if (fbo->format() == requestFormat) {
111 // choose the fbo with a matching format and the closest size
112 if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo))
113 candidate = fbo;
114 }
115
116 if (candidate) {
117 m_fbos.removeOne(candidate);
118
119 const QSize fboSize = candidate->size();
120 QSize sz = fboSize;
121
122 if (sz.width() < requestSize.width())
123 sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5)));
124 if (sz.height() < requestSize.height())
125 sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5)));
126
127 // wasting too much space?
128 if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 4)
129 sz = requestSize;
130
131 if (sz != fboSize) {
132 delete candidate;
133 candidate = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(sz), requestFormat);
134 }
135
136 chosen = candidate;
137 }
138 }
139
140 if (!chosen) {
141 if (strictSize)
142 chosen = new QGLFramebufferObject(requestSize, requestFormat);
143 else
144 chosen = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(requestSize), requestFormat);
145 }
146
147 if (!chosen->isValid()) {
148 delete chosen;
149 chosen = 0;
150 }
151
152 return chosen;
153}
154
155void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo)
156{
157 if (fbo)
158 m_fbos << fbo;
159}
160
161
162QPaintEngine* QGLPixmapGLPaintDevice::paintEngine() const
163{
164 return data->paintEngine();
165}
166
167void QGLPixmapGLPaintDevice::beginPaint()
168{
169 if (!data->isValid())
170 return;
171
172 // QGLPaintDevice::beginPaint will store the current binding and replace
173 // it with m_thisFBO:
174 m_thisFBO = data->m_renderFbo->handle();
175 QGLPaintDevice::beginPaint();
176
177 Q_ASSERT(data->paintEngine()->type() == QPaintEngine::OpenGL2);
178
179 // QPixmap::fill() is deferred until now, where we actually need to do the fill:
180 if (data->needsFill()) {
181 const QColor &c = data->fillColor();
182 float alpha = c.alphaF();
183 glDisable(GL_SCISSOR_TEST);
184 glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha);
185 glClear(GL_COLOR_BUFFER_BIT);
186 }
187 else if (!data->isUninitialized()) {
188 // If the pixmap (GL Texture) has valid content (it has been
189 // uploaded from an image or rendered into before), we need to
190 // copy it from the texture to the render FBO.
191
192 glDisable(GL_DEPTH_TEST);
193 glDisable(GL_SCISSOR_TEST);
194 glDisable(GL_BLEND);
195
196#if !defined(QT_OPENGL_ES_2)
197 glMatrixMode(GL_MODELVIEW);
198 glLoadIdentity();
199
200 glMatrixMode(GL_PROJECTION);
201 glLoadIdentity();
202 glOrtho(0, data->width(), data->height(), 0, -999999, 999999);
203#endif
204
205 glViewport(0, 0, data->width(), data->height());
206
207 // Pass false to bind so it doesn't copy the FBO into the texture!
208 context()->drawTexture(QRect(0, 0, data->width(), data->height()), data->bind(false));
209 }
210}
211
212void QGLPixmapGLPaintDevice::endPaint()
213{
214 if (!data->isValid())
215 return;
216
217 data->copyBackFromRenderFbo(false);
218
219 // Base's endPaint will restore the previous FBO binding
220 QGLPaintDevice::endPaint();
221
222 qgl_fbo_pool()->release(data->m_renderFbo);
223 data->m_renderFbo = 0;
224}
225
226QGLContext* QGLPixmapGLPaintDevice::context() const
227{
228 data->ensureCreated();
229 return data->m_ctx;
230}
231
232QSize QGLPixmapGLPaintDevice::size() const
233{
234 return data->size();
235}
236
237void QGLPixmapGLPaintDevice::setPixmapData(QGLPixmapData* d)
238{
239 data = d;
240}
241
242static int qt_gl_pixmap_serial = 0;
243
244QGLPixmapData::QGLPixmapData(PixelType type)
245 : QPixmapData(type, OpenGLClass)
246 , m_renderFbo(0)
247 , m_engine(0)
248 , m_ctx(0)
249 , m_dirty(false)
250 , m_hasFillColor(false)
251 , m_hasAlpha(false)
252{
253 setSerialNumber(++qt_gl_pixmap_serial);
254 m_glDevice.setPixmapData(this);
255}
256
257QGLPixmapData::~QGLPixmapData()
258{
259 QGLWidget *shareWidget = qt_gl_share_widget();
260 if (!shareWidget)
261 return;
262
263 delete m_engine;
264
265 if (m_texture.id) {
266 QGLShareContextScope ctx(shareWidget->context());
267 glDeleteTextures(1, &m_texture.id);
268 }
269}
270
271QPixmapData *QGLPixmapData::createCompatiblePixmapData() const
272{
273 return new QGLPixmapData(pixelType());
274}
275
276bool QGLPixmapData::isValid() const
277{
278 return w > 0 && h > 0;
279}
280
281bool QGLPixmapData::isValidContext(const QGLContext *ctx) const
282{
283 if (ctx == m_ctx)
284 return true;
285
286 const QGLContext *share_ctx = qt_gl_share_widget()->context();
287 return ctx == share_ctx || QGLContext::areSharing(ctx, share_ctx);
288}
289
290void QGLPixmapData::resize(int width, int height)
291{
292 if (width == w && height == h)
293 return;
294
295 if (width <= 0 || height <= 0) {
296 width = 0;
297 height = 0;
298 }
299
300 w = width;
301 h = height;
302 is_null = (w <= 0 || h <= 0);
303 d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
304
305 if (m_texture.id) {
306 QGLShareContextScope ctx(qt_gl_share_widget()->context());
307 glDeleteTextures(1, &m_texture.id);
308 m_texture.id = 0;
309 }
310
311 m_source = QImage();
312 m_dirty = isValid();
313 setSerialNumber(++qt_gl_pixmap_serial);
314}
315
316void QGLPixmapData::ensureCreated() const
317{
318 if (!m_dirty)
319 return;
320
321 m_dirty = false;
322
323 QGLShareContextScope ctx(qt_gl_share_widget()->context());
324 m_ctx = ctx;
325
326 const GLenum internal_format = m_hasAlpha ? GL_RGBA : GL_RGB;
327#ifdef QT_OPENGL_ES_2
328 const GLenum external_format = internal_format;
329#else
330 const GLenum external_format = qt_gl_preferredTextureFormat();
331#endif
332 const GLenum target = GL_TEXTURE_2D;
333
334 if (!m_texture.id) {
335 glGenTextures(1, &m_texture.id);
336 glBindTexture(target, m_texture.id);
337 glTexImage2D(target, 0, internal_format, w, h, 0, external_format, GL_UNSIGNED_BYTE, 0);
338 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
339 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
340 }
341
342 if (!m_source.isNull()) {
343 if (external_format == GL_RGB) {
344 const QImage tx = m_source.convertToFormat(QImage::Format_RGB888);
345
346 glBindTexture(target, m_texture.id);
347 glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
348 GL_UNSIGNED_BYTE, tx.bits());
349 } else {
350 const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, external_format);
351
352 glBindTexture(target, m_texture.id);
353 glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
354 GL_UNSIGNED_BYTE, tx.bits());
355 }
356
357 if (useFramebufferObjects())
358 m_source = QImage();
359 }
360
361 m_texture.options &= ~QGLContext::MemoryManagedBindOption;
362}
363
364void QGLPixmapData::fromImage(const QImage &image,
365 Qt::ImageConversionFlags /*flags*/)
366{
367 if (image.size() == QSize(w, h))
368 setSerialNumber(++qt_gl_pixmap_serial);
369 resize(image.width(), image.height());
370
371 if (pixelType() == BitmapType) {
372 m_source = image.convertToFormat(QImage::Format_MonoLSB);
373
374 } else {
375 QImage::Format format = QImage::Format_RGB32;
376 if (qApp->desktop()->depth() == 16)
377 format = QImage::Format_RGB16;
378
379 if (image.hasAlphaChannel() && const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels())
380 format = QImage::Format_ARGB32_Premultiplied;;
381
382 m_source = image.convertToFormat(format);
383 }
384
385 m_dirty = true;
386 m_hasFillColor = false;
387
388 m_hasAlpha = m_source.hasAlphaChannel();
389 w = image.width();
390 h = image.height();
391 is_null = (w <= 0 || h <= 0);
392 d = m_source.depth();
393
394 if (m_texture.id) {
395 QGLShareContextScope ctx(qt_gl_share_widget()->context());
396 glDeleteTextures(1, &m_texture.id);
397 m_texture.id = 0;
398 }
399}
400
401bool QGLPixmapData::fromFile(const QString &filename, const char *format,
402 Qt::ImageConversionFlags flags)
403{
404 if (pixelType() == QPixmapData::BitmapType)
405 return QPixmapData::fromFile(filename, format, flags);
406 QFile file(filename);
407 if (!file.open(QIODevice::ReadOnly))
408 return false;
409 QByteArray data = file.peek(64);
410 bool alpha;
411 if (m_texture.canBindCompressedTexture
412 (data.constData(), data.size(), format, &alpha)) {
413 resize(0, 0);
414 data = file.readAll();
415 file.close();
416 QGLShareContextScope ctx(qt_gl_share_widget()->context());