1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 ** This file is part of the QtOpenGL module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
10 ** This file contains pre-release code and may not be distributed.
11 ** You may use this file in accordance with the terms and conditions
12 ** contained in the either Technology Preview License Agreement or the
13 ** Beta Release License Agreement.
15 ** GNU Lesser General Public License Usage
16 ** Alternatively, this file may be used under the terms of the GNU Lesser
17 ** General Public License version 2.1 as published by the Free Software
18 ** Foundation and appearing in the file LICENSE.LGPL included in the
19 ** packaging of this file. Please review the following information to
20 ** ensure the GNU Lesser General Public License version 2.1 requirements
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 ** In addition, as a special exception, Nokia gives you certain
24 ** additional rights. These rights are described in the Nokia Qt LGPL
25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
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.
36 ** If you are unsure which license is appropriate for your use, please
37 ** contact the sales department at http://www.qtsoftware.com/contact.
40 ****************************************************************************/
43 #include "qglframebufferobject.h"
45 #include <private/qpaintengine_raster_p.h>
47 #include "qpixmapdata_gl_p.h"
49 #include <private/qgl_p.h>
50 #include <private/qdrawhelper_p.h>
52 #include <private/qpaintengineex_opengl2_p.h>
56 extern QGLWidget
* qt_gl_share_widget();
58 class QGLShareContextScope
61 QGLShareContextScope(const QGLContext
*ctx
)
64 QGLContext
*currentContext
= const_cast<QGLContext
*>(QGLContext::currentContext());
65 if (currentContext
!= ctx
&& !qgl_share_reg()->checkSharing(ctx
, currentContext
)) {
66 m_oldContext
= currentContext
;
67 m_ctx
= const_cast<QGLContext
*>(ctx
);
70 m_ctx
= currentContext
;
74 operator QGLContext
*()
79 QGLContext
*operator->()
84 ~QGLShareContextScope()
87 m_oldContext
->makeCurrent();
91 QGLContext
*m_oldContext
;
95 static int qt_gl_pixmap_serial
= 0;
97 QGLPixmapData::QGLPixmapData(PixelType type
)
98 : QPixmapData(type
, OpenGLClass
)
104 , m_hasFillColor(false)
107 setSerialNumber(++qt_gl_pixmap_serial
);
110 QGLPixmapData::~QGLPixmapData()
112 QGLWidget
*shareWidget
= qt_gl_share_widget();
117 QGLShareContextScope
ctx(shareWidget
->context());
118 glDeleteTextures(1, &m_textureId
);
122 bool QGLPixmapData::isValid() const
124 return w
> 0 && h
> 0;
127 bool QGLPixmapData::isValidContext(const QGLContext
*ctx
) const
132 const QGLContext
*share_ctx
= qt_gl_share_widget()->context();
133 return ctx
== share_ctx
|| qgl_share_reg()->checkSharing(ctx
, share_ctx
);
136 void QGLPixmapData::resize(int width
, int height
)
138 if (width
== w
&& height
== h
)
141 if (width
<= 0 || height
<= 0) {
148 is_null
= (w
<= 0 || h
<= 0);
149 d
= pixelType() == QPixmapData::PixmapType
? 32 : 1;
152 QGLShareContextScope
ctx(qt_gl_share_widget()->context());
153 glDeleteTextures(1, &m_textureId
);
159 setSerialNumber(++qt_gl_pixmap_serial
);
162 void QGLPixmapData::ensureCreated() const
169 QGLShareContextScope
ctx(qt_gl_share_widget()->context());
172 const GLenum format
= qt_gl_preferredTextureFormat();
173 const GLenum target
= GL_TEXTURE_2D
;
176 glGenTextures(1, &m_textureId
);
177 glBindTexture(target
, m_textureId
);
178 GLenum format
= m_hasAlpha
? GL_RGBA
: GL_RGB
;
179 glTexImage2D(target
, 0, format
, w
, h
, 0,
180 GL_RGBA
, GL_UNSIGNED_BYTE
, 0);
181 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
182 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
185 if (!m_source
.isNull()) {
186 const QImage tx
= ctx
->d_func()->convertToGLFormat(m_source
, true, format
);
188 glBindTexture(target
, m_textureId
);
189 glTexSubImage2D(target
, 0, 0, 0, w
, h
, format
,
190 GL_UNSIGNED_BYTE
, tx
.bits());
192 if (useFramebufferObjects())
197 QGLFramebufferObject
*QGLPixmapData::fbo() const
202 void QGLPixmapData::fromImage(const QImage
&image
,
203 Qt::ImageConversionFlags
)
205 if (image
.size() == QSize(w
, h
))
206 setSerialNumber(++qt_gl_pixmap_serial
);
207 resize(image
.width(), image
.height());
209 if (pixelType() == BitmapType
) {
210 m_source
= image
.convertToFormat(QImage::Format_MonoLSB
);
212 m_source
= image
.hasAlphaChannel()
213 ? image
.convertToFormat(QImage::Format_ARGB32_Premultiplied
)
214 : image
.convertToFormat(QImage::Format_RGB32
);
218 m_hasFillColor
= false;
220 m_hasAlpha
= image
.hasAlphaChannel();
223 is_null
= (w
<= 0 || h
<= 0);
224 d
= pixelType() == QPixmapData::PixmapType
? 32 : 1;
227 QGLShareContextScope
ctx(qt_gl_share_widget()->context());
228 glDeleteTextures(1, &m_textureId
);
233 bool QGLPixmapData::scroll(int dx
, int dy
, const QRect
&rect
)
241 void QGLPixmapData::copy(const QPixmapData
*data
, const QRect
&rect
)
243 if (data
->classId() != QPixmapData::OpenGLClass
) {
244 QPixmapData::copy(data
, rect
);
248 // can be optimized to do a framebuffer blit or similar ...
249 QPixmapData::copy(data
, rect
);
252 void QGLPixmapData::fill(const QColor
&color
)
258 m_hasAlpha
= color
.alpha() != 255;
260 if (useFramebufferObjects()) {
262 m_hasFillColor
= true;
265 QImage image
= fillImage(color
);
270 bool QGLPixmapData::hasAlphaChannel() const
275 QImage
QGLPixmapData::fillImage(const QColor
&color
) const
278 if (pixelType() == BitmapType
) {
279 img
= QImage(w
, h
, QImage::Format_MonoLSB
);
281 img
.setColor(0, QColor(Qt::color0
).rgba());
282 img
.setColor(1, QColor(Qt::color1
).rgba());
284 int gray
= qGray(color
.rgba());
285 if (qAbs(255 - gray
) < gray
)
292 ? QImage::Format_ARGB32_Premultiplied
293 : QImage::Format_RGB32
);
294 img
.fill(PREMUL(color
.rgba()));
299 QImage
QGLPixmapData::toImage() const
305 copyBackFromRenderFbo(true);
306 } else if (!m_source
.isNull()) {
308 } else if (m_dirty
|| m_hasFillColor
) {
309 return fillImage(m_fillColor
);
314 QGLShareContextScope
ctx(qt_gl_share_widget()->context());
315 extern QImage
qt_gl_read_texture(const QSize
&size
, bool alpha_format
, bool include_alpha
);
316 glBindTexture(GL_TEXTURE_2D
, m_textureId
);
317 return qt_gl_read_texture(QSize(w
, h
), true, true);
322 QGLFramebufferObject
*fbo
;
323 QGL2PaintEngineEx
*engine
;
326 static QVector
<TextureBuffer
> textureBufferStack
;
327 static int currentTextureBuffer
= 0;
329 void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound
) const
334 m_hasFillColor
= false;
336 const QGLContext
*share_ctx
= qt_gl_share_widget()->context();
337 QGLShareContextScope
ctx(share_ctx
);
341 if (!ctx
->d_ptr
->fbo
)
342 glGenFramebuffers(1, &ctx
->d_ptr
->fbo
);
344 glBindFramebuffer(GL_FRAMEBUFFER_EXT
, ctx
->d_ptr
->fbo
);
345 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT
, GL_COLOR_ATTACHMENT0_EXT
,
346 GL_TEXTURE_2D
, m_textureId
, 0);
353 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT
, m_renderFbo
->handle());
355 glDisable(GL_SCISSOR_TEST
);
357 glBlitFramebufferEXT(x0
, y0
, x1
, y1
,
362 if (keepCurrentFboBound
)
363 glBindFramebuffer(GL_FRAMEBUFFER_EXT
, ctx
->d_ptr
->current_fbo
);
366 void QGLPixmapData::swapBuffers()
371 copyBackFromRenderFbo(false);
372 m_renderFbo
->release();
374 --currentTextureBuffer
;
380 void QGLPixmapData::makeCurrent()
382 if (isValid() && m_renderFbo
)
386 void QGLPixmapData::doneCurrent()
388 if (isValid() && m_renderFbo
)
389 m_renderFbo
->release();
392 static TextureBuffer
createTextureBuffer(const QSize
&size
, QGL2PaintEngineEx
*engine
= 0)
394 TextureBuffer buffer
;
395 QGLFramebufferObjectFormat fmt
;
396 fmt
.setAttachment(QGLFramebufferObject::CombinedDepthStencil
);
399 buffer
.fbo
= new QGLFramebufferObject(size
, fmt
);
400 buffer
.engine
= engine
? engine
: new QGL2PaintEngineEx
;
405 bool QGLPixmapData::useFramebufferObjects()
407 return QGLFramebufferObject::hasOpenGLFramebufferObjects()
408 && QGLFramebufferObject::hasOpenGLFramebufferBlit()
409 && qt_gl_preferGL2Engine();
412 QPaintEngine
* QGLPixmapData::paintEngine() const
420 if (useFramebufferObjects()) {
421 extern QGLWidget
* qt_gl_share_widget();
423 if (!QGLContext::currentContext())
424 qt_gl_share_widget()->makeCurrent();
425 QGLShareContextScope
ctx(qt_gl_share_widget()->context());
427 if (textureBufferStack
.size() <= currentTextureBuffer
) {
428 textureBufferStack
<< createTextureBuffer(size());
430 QSize sz
= textureBufferStack
.at(currentTextureBuffer
).fbo
->size();
431 if (sz
.width() < w
|| sz
.height() < h
) {
433 sz
.setWidth(qMax(w
, qRound(sz
.width() * 1.5)));
435 sz
.setHeight(qMax(h
, qRound(sz
.height() * 1.5)));
437 // wasting too much space?
438 if (sz
.width() * sz
.height() > w
* h
* 2.5)
441 delete textureBufferStack
.at(currentTextureBuffer
).fbo
;
442 textureBufferStack
[currentTextureBuffer
] =
443 createTextureBuffer(sz
, textureBufferStack
.at(currentTextureBuffer
).engine
);
444 qDebug() << "Creating new pixmap texture buffer:" << sz
;
448 if (textureBufferStack
.at(currentTextureBuffer
).fbo
->isValid()) {
449 m_renderFbo
= textureBufferStack
.at(currentTextureBuffer
).fbo
;
450 m_engine
= textureBufferStack
.at(currentTextureBuffer
).engine
;
452 ++currentTextureBuffer
;
457 qWarning() << "Failed to create pixmap texture buffer of size " << size() << ", falling back to raster paint engine";
461 if (m_source
.size() != size())
462 m_source
= QImage(size(), QImage::Format_ARGB32_Premultiplied
);
463 if (m_hasFillColor
) {
464 m_source
.fill(PREMUL(m_fillColor
.rgba()));
465 m_hasFillColor
= false;
467 return m_source
.paintEngine();
470 GLuint
QGLPixmapData::bind(bool copyBack
) const
472 if (m_renderFbo
&& copyBack
) {
473 copyBackFromRenderFbo(true);
475 if (m_hasFillColor
) {
477 m_source
= QImage(w
, h
, QImage::Format_ARGB32_Premultiplied
);
478 m_source
.fill(PREMUL(m_fillColor
.rgba()));
479 m_hasFillColor
= false;
484 GLuint id
= m_textureId
;
485 glBindTexture(GL_TEXTURE_2D
, id
);
489 GLuint
QGLPixmapData::textureId() const
495 extern int qt_defaultDpiX();
496 extern int qt_defaultDpiY();
498 int QGLPixmapData::metric(QPaintDevice::PaintDeviceMetric metric
) const
504 case QPaintDevice::PdmWidth
:
506 case QPaintDevice::PdmHeight
:
508 case QPaintDevice::PdmNumColors
:
510 case QPaintDevice::PdmDepth
:
512 case QPaintDevice::PdmWidthMM
:
513 return qRound(w
* 25.4 / qt_defaultDpiX());
514 case QPaintDevice::PdmHeightMM
:
515 return qRound(h
* 25.4 / qt_defaultDpiY());
516 case QPaintDevice::PdmDpiX
:
517 case QPaintDevice::PdmPhysicalDpiX
:
518 return qt_defaultDpiX();
519 case QPaintDevice::PdmDpiY
:
520 case QPaintDevice::PdmPhysicalDpiY
:
521 return qt_defaultDpiY();
523 qWarning("QGLPixmapData::metric(): Invalid metric");