Re-apply change 353dcfd307853da289fdd245410e2e07358624a0 by Friedemann Kleint
[qt-netbsd.git] / src / opengl / qpixmapdata_gl.cpp
blob8a2187c88924a713e1736dba2b80e1b2f831a4c9
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: Nokia Corporation (qt-info@nokia.com)
5 **
6 ** This file is part of the QtOpenGL module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** No Commercial Usage
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
26 ** package.
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.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #include "qpixmap.h"
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>
54 QT_BEGIN_NAMESPACE
56 extern QGLWidget* qt_gl_share_widget();
58 class QGLShareContextScope
60 public:
61 QGLShareContextScope(const QGLContext *ctx)
62 : m_oldContext(0)
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);
68 m_ctx->makeCurrent();
69 } else {
70 m_ctx = currentContext;
74 operator QGLContext *()
76 return m_ctx;
79 QGLContext *operator->()
81 return m_ctx;
84 ~QGLShareContextScope()
86 if (m_oldContext)
87 m_oldContext->makeCurrent();
90 private:
91 QGLContext *m_oldContext;
92 QGLContext *m_ctx;
95 static int qt_gl_pixmap_serial = 0;
97 QGLPixmapData::QGLPixmapData(PixelType type)
98 : QPixmapData(type, OpenGLClass)
99 , m_renderFbo(0)
100 , m_textureId(0)
101 , m_engine(0)
102 , m_ctx(0)
103 , m_dirty(false)
104 , m_hasFillColor(false)
105 , m_hasAlpha(false)
107 setSerialNumber(++qt_gl_pixmap_serial);
110 QGLPixmapData::~QGLPixmapData()
112 QGLWidget *shareWidget = qt_gl_share_widget();
113 if (!shareWidget)
114 return;
116 if (m_textureId) {
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
129 if (ctx == m_ctx)
130 return true;
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)
139 return;
141 if (width <= 0 || height <= 0) {
142 width = 0;
143 height = 0;
146 w = width;
147 h = height;
148 is_null = (w <= 0 || h <= 0);
149 d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
151 if (m_textureId) {
152 QGLShareContextScope ctx(qt_gl_share_widget()->context());
153 glDeleteTextures(1, &m_textureId);
154 m_textureId = 0;
157 m_source = QImage();
158 m_dirty = isValid();
159 setSerialNumber(++qt_gl_pixmap_serial);
162 void QGLPixmapData::ensureCreated() const
164 if (!m_dirty)
165 return;
167 m_dirty = false;
169 QGLShareContextScope ctx(qt_gl_share_widget()->context());
170 m_ctx = ctx;
172 const GLenum format = qt_gl_preferredTextureFormat();
173 const GLenum target = GL_TEXTURE_2D;
175 if (!m_textureId) {
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())
193 m_source = QImage();
197 QGLFramebufferObject *QGLPixmapData::fbo() const
199 return m_renderFbo;
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);
211 } else {
212 m_source = image.hasAlphaChannel()
213 ? image.convertToFormat(QImage::Format_ARGB32_Premultiplied)
214 : image.convertToFormat(QImage::Format_RGB32);
217 m_dirty = true;
218 m_hasFillColor = false;
220 m_hasAlpha = image.hasAlphaChannel();
221 w = image.width();
222 h = image.height();
223 is_null = (w <= 0 || h <= 0);
224 d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
226 if (m_textureId) {
227 QGLShareContextScope ctx(qt_gl_share_widget()->context());
228 glDeleteTextures(1, &m_textureId);
229 m_textureId = 0;
233 bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect)
235 Q_UNUSED(dx);
236 Q_UNUSED(dy);
237 Q_UNUSED(rect);
238 return false;
241 void QGLPixmapData::copy(const QPixmapData *data, const QRect &rect)
243 if (data->classId() != QPixmapData::OpenGLClass) {
244 QPixmapData::copy(data, rect);
245 return;
248 // can be optimized to do a framebuffer blit or similar ...
249 QPixmapData::copy(data, rect);
252 void QGLPixmapData::fill(const QColor &color)
254 if (!isValid())
255 return;
257 if (!m_textureId)
258 m_hasAlpha = color.alpha() != 255;
260 if (useFramebufferObjects()) {
261 m_source = QImage();
262 m_hasFillColor = true;
263 m_fillColor = color;
264 } else {
265 QImage image = fillImage(color);
266 fromImage(image, 0);
270 bool QGLPixmapData::hasAlphaChannel() const
272 return m_hasAlpha;
275 QImage QGLPixmapData::fillImage(const QColor &color) const
277 QImage img;
278 if (pixelType() == BitmapType) {
279 img = QImage(w, h, QImage::Format_MonoLSB);
280 img.setNumColors(2);
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)
286 img.fill(0);
287 else
288 img.fill(1);
289 } else {
290 img = QImage(w, h,
291 m_hasAlpha
292 ? QImage::Format_ARGB32_Premultiplied
293 : QImage::Format_RGB32);
294 img.fill(PREMUL(color.rgba()));
296 return img;
299 QImage QGLPixmapData::toImage() const
301 if (!isValid())
302 return QImage();
304 if (m_renderFbo) {
305 copyBackFromRenderFbo(true);
306 } else if (!m_source.isNull()) {
307 return m_source;
308 } else if (m_dirty || m_hasFillColor) {
309 return fillImage(m_fillColor);
310 } else {
311 ensureCreated();
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);
320 struct TextureBuffer
322 QGLFramebufferObject *fbo;
323 QGL2PaintEngineEx *engine;
326 static QVector<TextureBuffer> textureBufferStack;
327 static int currentTextureBuffer = 0;
329 void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const
331 if (!isValid())
332 return;
334 m_hasFillColor = false;
336 const QGLContext *share_ctx = qt_gl_share_widget()->context();
337 QGLShareContextScope ctx(share_ctx);
339 ensureCreated();
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);
348 const int x0 = 0;
349 const int x1 = w;
350 const int y0 = 0;
351 const int y1 = h;
353 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, m_renderFbo->handle());
355 glDisable(GL_SCISSOR_TEST);
357 glBlitFramebufferEXT(x0, y0, x1, y1,
358 x0, y0, x1, y1,
359 GL_COLOR_BUFFER_BIT,
360 GL_NEAREST);
362 if (keepCurrentFboBound)
363 glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
366 void QGLPixmapData::swapBuffers()
368 if (!isValid())
369 return;
371 copyBackFromRenderFbo(false);
372 m_renderFbo->release();
374 --currentTextureBuffer;
376 m_renderFbo = 0;
377 m_engine = 0;
380 void QGLPixmapData::makeCurrent()
382 if (isValid() && m_renderFbo)
383 m_renderFbo->bind();
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);
397 fmt.setSamples(4);
399 buffer.fbo = new QGLFramebufferObject(size, fmt);
400 buffer.engine = engine ? engine : new QGL2PaintEngineEx;
402 return buffer;
405 bool QGLPixmapData::useFramebufferObjects()
407 return QGLFramebufferObject::hasOpenGLFramebufferObjects()
408 && QGLFramebufferObject::hasOpenGLFramebufferBlit()
409 && qt_gl_preferGL2Engine();
412 QPaintEngine* QGLPixmapData::paintEngine() const
414 if (!isValid())
415 return 0;
417 if (m_engine)
418 return m_engine;
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());
429 } else {
430 QSize sz = textureBufferStack.at(currentTextureBuffer).fbo->size();
431 if (sz.width() < w || sz.height() < h) {
432 if (sz.width() < w)
433 sz.setWidth(qMax(w, qRound(sz.width() * 1.5)));
434 if (sz.height() < h)
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)
439 sz = QSize(w, h);
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;
454 return m_engine;
457 qWarning() << "Failed to create pixmap texture buffer of size " << size() << ", falling back to raster paint engine";
460 m_dirty = true;
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);
474 } else {
475 if (m_hasFillColor) {
476 m_dirty = true;
477 m_source = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
478 m_source.fill(PREMUL(m_fillColor.rgba()));
479 m_hasFillColor = false;
481 ensureCreated();
484 GLuint id = m_textureId;
485 glBindTexture(GL_TEXTURE_2D, id);
486 return id;
489 GLuint QGLPixmapData::textureId() const
491 ensureCreated();
492 return m_textureId;
495 extern int qt_defaultDpiX();
496 extern int qt_defaultDpiY();
498 int QGLPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
500 if (w == 0)
501 return 0;
503 switch (metric) {
504 case QPaintDevice::PdmWidth:
505 return w;
506 case QPaintDevice::PdmHeight:
507 return h;
508 case QPaintDevice::PdmNumColors:
509 return 0;
510 case QPaintDevice::PdmDepth:
511 return d;
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();
522 default:
523 qWarning("QGLPixmapData::metric(): Invalid metric");
524 return 0;
528 QT_END_NAMESPACE