summaryrefslogtreecommitdiffstats
path: root/src/opengl/qpixmapdata_gl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl/qpixmapdata_gl.cpp')
-rw-r--r--src/opengl/qpixmapdata_gl.cpp270
1 files changed, 195 insertions, 75 deletions
diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp
index b079557..e3af864 100644
--- a/src/opengl/qpixmapdata_gl.cpp
+++ b/src/opengl/qpixmapdata_gl.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qpixmap.h"
+#include "qglframebufferobject.h"
#include <private/qpaintengine_raster_p.h>
@@ -48,6 +49,8 @@
#include <private/qgl_p.h>
#include <private/qdrawhelper_p.h>
+#include <private/qpaintengineex_opengl2_p.h>
+
QT_BEGIN_NAMESPACE
extern QGLWidget* qt_gl_share_widget();
@@ -57,12 +60,14 @@ class QGLShareContextScope
public:
QGLShareContextScope(const QGLContext *ctx)
: m_oldContext(0)
- , m_ctx(const_cast<QGLContext *>(ctx))
{
- const QGLContext *currentContext = QGLContext::currentContext();
+ QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
if (currentContext != ctx && !qgl_share_reg()->checkSharing(ctx, currentContext)) {
- m_oldContext = const_cast<QGLContext *>(currentContext);
+ m_oldContext = currentContext;
+ m_ctx = const_cast<QGLContext *>(ctx);
m_ctx->makeCurrent();
+ } else {
+ m_ctx = currentContext;
}
}
@@ -87,47 +92,16 @@ private:
QGLContext *m_ctx;
};
-void qt_gl_convertFromGLImage(QImage *img)
-{
- const int w = img->width();
- const int h = img->height();
-
- if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
- uint *p = (uint*)img->bits();
- uint *end = p + w*h;
-
- while (p < end) {
- uint a = *p << 24;
- *p = (*p >> 8) | a;
- p++;
- }
-
- *img = img->mirrored();
- } else {
- // mirror image
- uint *data = (uint *)img->bits();
-
- const int mid = h/2;
-
- for (int y = 0; y < mid; ++y) {
- uint *p = data + y * w;
- uint *end = p + w;
- uint *q = data + (h - y - 1) * w;
-
- while (p < end)
- qSwap(*p++, *q++);
- }
- }
-}
-
-
static int qt_gl_pixmap_serial = 0;
QGLPixmapData::QGLPixmapData(PixelType type)
: QPixmapData(type, OpenGLClass)
, m_width(0)
, m_height(0)
- , m_texture(0)
+ , m_renderFbo(0)
+ , m_textureId(0)
+ , m_engine(0)
+ , m_ctx(0)
, m_dirty(false)
{
setSerialNumber(++qt_gl_pixmap_serial);
@@ -135,10 +109,11 @@ QGLPixmapData::QGLPixmapData(PixelType type)
QGLPixmapData::~QGLPixmapData()
{
- if (m_texture && qt_gl_share_widget()) {
- QGLShareContextScope ctx(qt_gl_share_widget()->context());
- glDeleteTextures(1, &m_texture);
- }
+ QGLWidget *shareWidget = qt_gl_share_widget();
+ if (!shareWidget)
+ return;
+ QGLShareContextScope ctx(shareWidget->context());
+ glDeleteTextures(1, &m_textureId);
}
bool QGLPixmapData::isValid() const
@@ -148,6 +123,9 @@ bool QGLPixmapData::isValid() const
bool QGLPixmapData::isValidContext(const QGLContext *ctx) const
{
+ if (ctx == m_ctx)
+ return true;
+
const QGLContext *share_ctx = qt_gl_share_widget()->context();
return ctx == share_ctx || qgl_share_reg()->checkSharing(ctx, share_ctx);
}
@@ -160,6 +138,12 @@ void QGLPixmapData::resize(int width, int height)
m_width = width;
m_height = height;
+ if (m_textureId) {
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ glDeleteTextures(1, &m_textureId);
+ m_textureId = 0;
+ }
+
m_source = QImage();
m_dirty = isValid();
setSerialNumber(++qt_gl_pixmap_serial);
@@ -173,28 +157,35 @@ void QGLPixmapData::ensureCreated() const
m_dirty = false;
QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ m_ctx = ctx;
const GLenum format = qt_gl_preferredTextureFormat();
- const GLenum target = qt_gl_preferredTextureTarget();
+ const GLenum target = GL_TEXTURE_2D;
- if (!m_texture)
- glGenTextures(1, &m_texture);
-
- glBindTexture(target, m_texture);
+ if (!m_textureId) {
+ glGenTextures(1, &m_textureId);
+ glBindTexture(target, m_textureId);
+ glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ }
- if (m_source.isNull()) {
- glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0, format, GL_UNSIGNED_BYTE, 0);
- } else {
+ if (!m_source.isNull()) {
const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, format);
- glBindTexture(target, m_texture);
- glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0, format,
- GL_UNSIGNED_BYTE, tx.bits());
+ glBindTexture(target, m_textureId);
+ glTexSubImage2D(target, 0, 0, 0, m_width, m_height, format,
+ GL_UNSIGNED_BYTE, tx.bits());
- m_source = QImage();
+ if (useFramebufferObjects())
+ m_source = QImage();
}
}
+QGLFramebufferObject *QGLPixmapData::fbo() const
+{
+ return m_renderFbo;
+}
+
void QGLPixmapData::fromImage(const QImage &image,
Qt::ImageConversionFlags)
{
@@ -213,6 +204,17 @@ bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect)
return false;
}
+void QGLPixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->classId() != QPixmapData::OpenGLClass) {
+ QPixmapData::copy(data, rect);
+ return;
+ }
+
+ // can be optimized to do a framebuffer blit or similar ...
+ QPixmapData::copy(data, rect);
+}
+
void QGLPixmapData::fill(const QColor &color)
{
if (!isValid())
@@ -239,29 +241,108 @@ QImage QGLPixmapData::toImage() const
if (!isValid())
return QImage();
- if (!m_source.isNull())
+ if (m_renderFbo)
+ copyBackFromRenderFbo(true);
+ else if (!m_source.isNull())
return m_source;
else if (m_dirty)
return QImage(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
+ else
+ ensureCreated();
+
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+ extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha);
+ glBindTexture(GL_TEXTURE_2D, m_textureId);
+ return qt_gl_read_texture(QSize(m_width, m_height), true, true);
+}
+
+struct TextureBuffer
+{
+ QGLFramebufferObject *fbo;
+ QGL2PaintEngineEx *engine;
+};
+
+static QVector<TextureBuffer> textureBufferStack;
+static int currentTextureBuffer = 0;
+
+void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const
+{
+ if (!isValid())
+ return;
+
+ const QGLContext *share_ctx = qt_gl_share_widget()->context();
+ QGLShareContextScope ctx(share_ctx);
ensureCreated();
- QGLShareContextScope ctx(qt_gl_share_widget()->context());
- QImage img(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
+ if (!ctx->d_ptr->fbo)
+ glGenFramebuffers(1, &ctx->d_ptr->fbo);
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, m_textureId, 0);
+
+ const int x0 = 0;
+ const int x1 = m_width;
+ const int y0 = 0;
+ const int y1 = m_height;
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, m_renderFbo->handle());
+
+ glDisable(GL_SCISSOR_TEST);
+
+ glBlitFramebufferEXT(x0, y0, x1, y1,
+ x0, y0, x1, y1,
+ GL_COLOR_BUFFER_BIT,
+ GL_NEAREST);
- GLenum format = qt_gl_preferredTextureFormat();
- GLenum target = qt_gl_preferredTextureTarget();
+ if (keepCurrentFboBound)
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
+}
+
+void QGLPixmapData::swapBuffers()
+{
+ if (!isValid())
+ return;
- glBindTexture(target, m_texture);
-#ifndef QT_OPENGL_ES
- glGetTexImage(target, 0, format, GL_UNSIGNED_BYTE, img.bits());
-#else
- // XXX - cannot download textures this way on OpenGL/ES.
-#endif
+ copyBackFromRenderFbo(false);
+ m_renderFbo->release();
- qt_gl_convertFromGLImage(&img);
+ --currentTextureBuffer;
- return img;
+ m_renderFbo = 0;
+ m_engine = 0;
+}
+
+void QGLPixmapData::makeCurrent()
+{
+ if (isValid() && m_renderFbo)
+ m_renderFbo->bind();
+}
+
+void QGLPixmapData::doneCurrent()
+{
+ if (isValid() && m_renderFbo)
+ m_renderFbo->release();
+}
+
+static TextureBuffer createTextureBuffer(const QSize &size, QGL2PaintEngineEx *engine = 0)
+{
+ TextureBuffer buffer;
+ QGLFramebufferObjectFormat fmt;
+ fmt.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ fmt.setSamples(4);
+
+ buffer.fbo = new QGLFramebufferObject(size, fmt);
+ buffer.engine = engine ? engine : new QGL2PaintEngineEx;
+
+ return buffer;
+}
+
+bool QGLPixmapData::useFramebufferObjects()
+{
+ return QGLFramebufferObject::hasOpenGLFramebufferObjects()
+ && QGLFramebufferObject::hasOpenGLFramebufferBlit();
}
QPaintEngine* QGLPixmapData::paintEngine() const
@@ -269,23 +350,62 @@ QPaintEngine* QGLPixmapData::paintEngine() const
if (!isValid())
return 0;
- m_source = toImage();
- m_dirty = true;
+ if (m_engine)
+ return m_engine;
+ else if (!useFramebufferObjects()) {
+ m_dirty = true;
+
+ if (m_source.size() != size())
+ m_source = QImage(size(), QImage::Format_ARGB32_Premultiplied);
+ return m_source.paintEngine();
+ }
- return m_source.paintEngine();
+ extern QGLWidget* qt_gl_share_widget();
+
+ if (!QGLContext::currentContext())
+ qt_gl_share_widget()->makeCurrent();
+ QGLShareContextScope ctx(qt_gl_share_widget()->context());
+
+ if (textureBufferStack.size() <= currentTextureBuffer) {
+ textureBufferStack << createTextureBuffer(size());
+ } else {
+ QSize sz = textureBufferStack.at(currentTextureBuffer).fbo->size();
+ if (sz.width() < m_width || sz.height() < m_height) {
+ if (sz.width() < m_width)
+ sz.setWidth(qMax(m_width, qRound(sz.width() * 1.5)));
+ if (sz.height() < m_height)
+ sz.setHeight(qMax(m_height, qRound(sz.height() * 1.5)));
+ delete textureBufferStack.at(currentTextureBuffer).fbo;
+ textureBufferStack[currentTextureBuffer] =
+ createTextureBuffer(sz, textureBufferStack.at(currentTextureBuffer).engine);
+ qDebug() << "Creating new pixmap texture buffer:" << sz;
+ }
+ }
+
+ m_renderFbo = textureBufferStack.at(currentTextureBuffer).fbo;
+ m_engine = textureBufferStack.at(currentTextureBuffer).engine;
+
+ ++currentTextureBuffer;
+
+ return m_engine;
}
-GLuint QGLPixmapData::bind() const
+GLuint QGLPixmapData::bind(bool copyBack) const
{
- ensureCreated();
- glBindTexture(qt_gl_preferredTextureTarget(), m_texture);
- return m_texture;
+ if (m_renderFbo && copyBack)
+ copyBackFromRenderFbo(true);
+ else
+ ensureCreated();
+
+ GLuint id = m_textureId;
+ glBindTexture(GL_TEXTURE_2D, id);
+ return id;
}
GLuint QGLPixmapData::textureId() const
{
ensureCreated();
- return m_texture;
+ return m_textureId;
}
extern int qt_defaultDpiX();