1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtOpenGL module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
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#include <private/qfont_p.h>
53
54#include <private/qpaintengineex_opengl2_p.h>
55
56#include <qdesktopwidget.h>
57#include <qfile.h>
58#include <qimagereader.h>
59#include <qbuffer.h>
60
61QT_BEGIN_NAMESPACE
62
63Q_OPENGL_EXPORT extern const QGLContext* qt_gl_share_context();
64
65/*!
66 \class QGLFramebufferObjectPool
67 \since 4.6
68
69 \brief The QGLFramebufferObject class provides a pool of framebuffer
70 objects for offscreen rendering purposes.
71
72 When requesting an FBO of a given size and format, an FBO of the same
73 format and a size at least as big as the requested size will be returned.
74
75 \internal
76*/
77
78static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo)
79{
80 return qAbs(size.width() * size.height() - fbo->width() * fbo->height());
81}
82
83extern int qt_next_power_of_two(int v);
84
85static inline QSize maybeRoundToNextPowerOfTwo(const QSize &sz)
86{
87#ifdef QT_OPENGL_ES_2
88 QSize rounded(qt_next_power_of_two(sz.width()), qt_next_power_of_two(sz.height()));
89 if (rounded.width() * rounded.height() < 1.20 * sz.width() * sz.height())
90 return rounded;
91#endif
92 return sz;
93}
94
95
96QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat, bool strictSize)
97{
98 QGLFramebufferObject *chosen = 0;
99 QGLFramebufferObject *candidate = 0;
100 for (int i = 0; !chosen && i < m_fbos.size(); ++i) {
101 QGLFramebufferObject *fbo = m_fbos.at(i);
102
103 if (strictSize) {
104 if (fbo->size() == requestSize && fbo->format() == requestFormat) {
105 chosen = fbo;
106 break;
107 } else {
108 continue;
109 }
110 }
111
112 if (fbo->format() == requestFormat) {
113 // choose the fbo with a matching format and the closest size
114 if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo))
115 candidate = fbo;
116 }
117
118 if (candidate) {
119 m_fbos.removeOne(candidate);
120
121 const QSize fboSize = candidate->size();
122 QSize sz = fboSize;
123
124 if (sz.width() < requestSize.width())
125 sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5)));
126 if (sz.height() < requestSize.height())
127 sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5)));
128
129 // wasting too much space?
130 if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 4)
131 sz = requestSize;
132
133 if (sz != fboSize) {
134 delete candidate;
135 candidate = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(sz), requestFormat);
136 }
137
138 chosen = candidate;
139 }
140 }
141
142 if (!chosen) {
143 if (strictSize)
144 chosen = new QGLFramebufferObject(requestSize, requestFormat);
145 else
146 chosen = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(requestSize), requestFormat);
147 }
148
149 if (!chosen->isValid()) {
150 delete chosen;
151 chosen = 0;
152 }
153
154 return chosen;
155}
156
157void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo)
158{
159 if (fbo)
160 m_fbos << fbo;
161}
162
163
164QPaintEngine* QGLPixmapGLPaintDevice::paintEngine() const
165{
166 return data->paintEngine();
167}
168
169void QGLPixmapGLPaintDevice::beginPaint()
170{
171 if (!data->isValid())
172 return;
173
174 // QGLPaintDevice::beginPaint will store the current binding and replace
175 // it with m_thisFBO:
176 m_thisFBO = data->m_renderFbo->handle();
177 QGLPaintDevice::beginPaint();
178
179 Q_ASSERT(data->paintEngine()->type() == QPaintEngine::OpenGL2);
180
181 // QPixmap::fill() is deferred until now, where we actually need to do the fill:
182 if (data->needsFill()) {
183 const QColor &c = data->fillColor();
184 float alpha = c.alphaF();
185 glDisable(GL_SCISSOR_TEST);
186 glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha);
187 glClear(GL_COLOR_BUFFER_BIT);
188 }
189 else if (!data->isUninitialized()) {
190 // If the pixmap (GL Texture) has valid content (it has been
191 // uploaded from an image or rendered into before), we need to
192 // copy it from the texture to the render FBO.
193
194 glDisable(GL_DEPTH_TEST);
195 glDisable(GL_SCISSOR_TEST);
196 glDisable(GL_BLEND);
197
198#if !defined(QT_OPENGL_ES_2)
199 glMatrixMode(GL_MODELVIEW);
200 glLoadIdentity();
201
202 glMatrixMode(GL_PROJECTION);
203 glLoadIdentity();
204 glOrtho(0, data->width(), data->height(), 0, -999999, 999999);
205#endif
206
207 glViewport(0, 0, data->width(), data->height());
208
209 // Pass false to bind so it doesn't copy the FBO into the texture!
210 context()->drawTexture(QRect(0, 0, data->width(), data->height()), data->bind(false));
211 }
212}
213
214void QGLPixmapGLPaintDevice::endPaint()
215{
216 if (!data->isValid())
217 return;
218
219 data->copyBackFromRenderFbo(false);
220
221 // Base's endPaint will restore the previous FBO binding
222 QGLPaintDevice::endPaint();
223
224 qgl_fbo_pool()->release(data->m_renderFbo);
225 data->m_renderFbo = 0;
226}
227
228QGLContext* QGLPixmapGLPaintDevice::context() const
229{
230 data->ensureCreated();
231 return data->m_ctx;
232}
233
234QSize QGLPixmapGLPaintDevice::size() const
235{
236 return data->size();
237}
238
239bool QGLPixmapGLPaintDevice::alphaRequested() const
240{
241 return data->m_hasAlpha;
242}
243
244void QGLPixmapGLPaintDevice::setPixmapData(QGLPixmapData* d)
245{
246 data = d;
247}
248
249static int qt_gl_pixmap_serial = 0;
250
251QGLPixmapData::QGLPixmapData(PixelType type)
252 : QPixmapData(type, OpenGLClass)
253 , m_renderFbo(0)
254 , m_engine(0)
255 , m_ctx(0)
256 , m_dirty(false)
257 , m_hasFillColor(false)
258 , m_hasAlpha(false)
259{
260 setSerialNumber(++qt_gl_pixmap_serial);
261 m_glDevice.setPixmapData(this);
262}
263
264QGLPixmapData::~QGLPixmapData()
265{
266 const QGLContext *shareContext = qt_gl_share_context();
267 if (!shareContext)
268 return;
269
270 delete m_engine;
271
272 if (m_texture.id) {
273 QGLShareContextScope ctx(shareContext);
274 glDeleteTextures(1, &m_texture.id);
275 }
276}
277
278QPixmapData *QGLPixmapData::createCompatiblePixmapData() const
279{
280 return new QGLPixmapData(pixelType());
281}
282
283bool QGLPixmapData::isValid() const
284{
285 return w > 0 && h > 0;
286}
287
288bool QGLPixmapData::isValidContext(const QGLContext *ctx) const
289{
290 if (ctx == m_ctx)
291 return true;
292
293 const QGLContext *share_ctx = qt_gl_share_context();
294 return ctx == share_ctx || QGLContext::areSharing(ctx, share_ctx);
295}
296
297void QGLPixmapData::resize(int width, int height)
298{
299 if (width == w && height == h)
300 return;
301
302 if (width <= 0 || height <= 0) {
303 width = 0;
304 height = 0;
305 }
306
307 w = width;
308 h = height;
309 is_null = (w <= 0 || h <= 0);
310 d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
311
312 if (m_texture.id) {
313 QGLShareContextScope ctx(qt_gl_share_context());
314 glDeleteTextures(1, &m_texture.id);
315 m_texture.id = 0;
316 }
317
318 m_source = QImage();
319 m_dirty = isValid();
320 setSerialNumber(++qt_gl_pixmap_serial);
321}
322
323void QGLPixmapData::ensureCreated() const
324{
325 if (!m_dirty)
326 return;
327
328 m_dirty = false;
329
330 QGLShareContextScope ctx(qt_gl_share_context());
331 m_ctx = ctx;
332
333 const GLenum internal_format = m_hasAlpha ? GL_RGBA : GL_RGB;
334#ifdef QT_OPENGL_ES_2
335 const GLenum external_format = internal_format;
336#else
337 const GLenum external_format = qt_gl_preferredTextureFormat();
338#endif
339 const GLenum target = GL_TEXTURE_2D;
340
341 if (!m_texture.id) {
342 glGenTextures(1, &m_texture.id);
343 glBindTexture(target, m_texture.id);
344 glTexImage2D(target, 0, internal_format, w, h, 0, external_format, GL_UNSIGNED_BYTE, 0);
345 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
346 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
347 }
348
349 if (!m_source.isNull()) {
350 if (external_format == GL_RGB) {
351 const QImage tx = m_source.convertToFormat(QImage::Format_RGB888).mirrored(false, true);
352
353 glBindTexture(target, m_texture.id);
354 glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
355 GL_UNSIGNED_BYTE, tx.bits());
356 } else {
357 const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, external_format);
358
359 glBindTexture(target, m_texture.id);
360 glTexSubImage2D(target, 0, 0, 0, w, h, external_format,
361 GL_UNSIGNED_BYTE, tx.bits());
362 }
363
364 if (useFramebufferObjects())
365 m_source = QImage();
366 }
367
368 m_texture.options &= ~QGLContext::MemoryManagedBindOption;
369}
370
371void QGLPixmapData::fromImage(const QImage &image,
372 Qt::ImageConversionFlags flags)
373{
374 QImage img = image;
375 createPixmapForImage(img, flags, false);
376}
377
378void QGLPixmapData::fromImageReader(QImageReader *imageReader,
379 Qt::ImageConversionFlags flags)
380{
381 QImage image = imageReader->read();
382 if (image.isNull())
383 return;
384
385 createPixmapForImage(image, flags, true);
386}
387
388bool QGLPixmapData::fromFile(const QString &filename, const char *format,
389 Qt::ImageConversionFlags flags)
390{
391 if (pixelType() == QPixmapData::BitmapType)
392 return QPixmapData::fromFile(filename, format, flags);
393 QFile file(filename);
394 if (file.open(QIODevice::ReadOnly)) {
395 QByteArray data = file.peek(64);
396 bool alpha;
397 if (m_texture.canBindCompressedTexture
398 (data.constData(), data.size(), format, &alpha)) {
399 resize(0, 0);
400 data = file.readAll();
401 file.close();
402 QGLShareContextScope ctx(qt_gl_share_context());
403 QSize size = m_texture.bindCompressedTexture
404 (data.constData(), data.size(), format);
405 if (!size.isEmpty()) {
406 w = size.width();
407 h = size.height();
408 is_null = false;
409 d = 32;
410 m_hasAlpha = alpha;
411 m_source = QImage();
412 m_dirty = isValid();
413 return true;
414 }
415 return false;
416 }
417 }
418
419 QImage image = QImageReader(filename, format).read();
420 if (image.isNull())
421 return false;
422
423 createPixmapForImage(image, flags, true);
424
425 return !isNull();
426}
427
428bool QGLPixmapData::fromData(const uchar *buffer, uint len, const char *format,
429 Qt::ImageConversionFlags flags)
430{
431 bool alpha;
432 const char *buf = reinterpret_cast<const char *>(buffer);
433 if (m_texture.canBindCompressedTexture(buf, int(len), format, &alpha)) {
434 resize(0, 0);
435 QGLShareContextScope ctx(qt_gl_share_context());
436 QSize size = m_texture.bindCompressedTexture(buf, int(len), format);
437 if (!size.isEmpty()) {
438 w = size.width();
439 h = size.height();
440 is_null = false;
441 d = 32;
442 m_hasAlpha = alpha;
443 m_source = QImage();
444 m_dirty = isValid();
445 return true;
446 }
447 }
448
449 QByteArray a = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer), len);
450 QBuffer b(&a);
451 b.open(QIODevice::ReadOnly);
452 QImage image = QImageReader(&b, format).read();
453 if (image.isNull())
454 return false;
455
456 createPixmapForImage(image, flags, true);
457
458 return !isNull();
459}
460
461/*!
462 out-of-place conversion (inPlace == false) will always detach()
463 */
464void QGLPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace)
465{
466 if (image.size() == QSize(w, h))
467 setSerialNumber(++qt_gl_pixmap_serial);
468
469 resize(image.width(), image.height());
470
471 if (pixelType() == BitmapType) {
472 m_source = image.convertToFormat(QImage::Format_MonoLSB);
473
474 } else {
475 QImage::Format format = QImage::Format_RGB32;
476 if (qApp->desktop()->depth() == 16)
477 format = QImage::Format_RGB16;
478
479 if (image.hasAlphaChannel()
480 && ((flags & Qt::NoOpaqueDetection)
481 || const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels()))
482 format = QImage::Format_ARGB32_Premultiplied;;
483
484 if (inPlace && image.data_ptr()->convertInPlace(format, flags)) {
485 m_source = image;
486 } else {
487 m_source = image.convertToFormat(format);
488
489 // convertToFormat won't detach the image if format stays the same.
490 if (image.format() == format)
491 m_source.detach();
492 }
493 }
494
495 m_dirty = true;
496 m_hasFillColor = false;
497
498 m_hasAlpha = m_source.hasAlphaChannel();
499 w = image.width();
500 h = image.height();
501 is_null = (w <= 0 || h <= 0);
502 d = m_source.depth();
503
504 if (m_texture.id) {
505 QGLShareContextScope ctx(qt_gl_share_context());
506 glDeleteTextures(1, &m_texture.id);
507 m_texture.id = 0;
508 }
509}
510
511bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect)
512{
513 Q_UNUSED(dx);
514 Q_UNUSED(dy);
515 Q_UNUSED(rect);
516 return false;
517}
518
519void QGLPixmapData::copy(const QPixmapData *data, const QRect &rect)
520{
521 if (data->classId() != QPixmapData::OpenGLClass || !static_cast<const QGLPixmapData *>(data)->useFramebufferObjects()) {
522 QPixmapData::copy(data, rect);
523 return;
524 }
525
526 const QGLPixmapData *other = static_cast<const QGLPixmapData *>(data);
527 if (other->m_renderFbo) {
528 QGLShareContextScope ctx(qt_gl_share_context());
529
530 resize(rect.width(), rect.height());
531 m_hasAlpha = other->m_hasAlpha;
532 ensureCreated();
533
534 if (!ctx->d_ptr->fbo)
535 glGenFramebuffers(1, &ctx->d_ptr->fbo);
536
537 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
538 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
539 GL_TEXTURE_2D, m_texture.id, 0);
540
541 if (!other->m_renderFbo->isBound())
542 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, other->m_renderFbo->handle());
543
544 glDisable(GL_SCISSOR_TEST);
545 if (ctx->d_ptr->active_engine && ctx->d_ptr->active_engine->type() == QPaintEngine::OpenGL2)
546 static_cast<QGL2PaintEngineEx *>(ctx->d_ptr->active_engine)->invalidateState();
547
548 glBlitFramebufferEXT(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height(),
549 0, 0, w, h,
550 GL_COLOR_BUFFER_BIT,
551 GL_NEAREST);
552
553 glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
554 } else {
555 QPixmapData::copy(data, rect);
556 }
557}
558
559void QGLPixmapData::fill(const QColor &color)
560{
561 if (!isValid())
562 return;
563
564 bool hasAlpha = color.alpha() != 255;
565 if (hasAlpha && !m_hasAlpha) {
566 if (m_texture.id) {
567 glDeleteTextures(1, &m_texture.id);
568 m_texture.id = 0;
569 m_dirty = true;
570 }
571 m_hasAlpha = color.alpha() != 255;
572 }
573
574 if (useFramebufferObjects()) {
575 m_source = QImage();
576 m_hasFillColor = true;
577 m_fillColor = color;
578 } else {
579
580 if (m_source.isNull()) {
581 m_fillColor = color;
582 m_hasFillColor = true;
583
584 } else if (m_source.depth() == 32) {
585 m_source.fill(PREMUL(color.rgba()));
586
587 } else if (m_source.depth() == 1) {
588 if (color == Qt::color1)
589 m_source.fill(1);
590 else
591 m_source.fill(0);
592 }
593 }
594}
595
596bool QGLPixmapData::hasAlphaChannel() const
597{
598 return m_hasAlpha;
599}
600
601QImage QGLPixmapData::fillImage(const QColor &color) const
602{
603 QImage img;
604 if (pixelType() == BitmapType) {
605 img = QImage(w, h, QImage::Format_MonoLSB);
606
607 img.setColorCount(2);
608 img.setColor(0, QColor(Qt::color0).rgba());
609 img.setColor(1, QColor(Qt::color1).rgba());
610
611 if (color == Qt::color1)
612 img.fill(1);
613 else
614 img.fill(0);
615 } else {
616 img = QImage(w, h,
617 m_hasAlpha
618 ? QImage::Format_ARGB32_Premultiplied
619 : QImage::Format_RGB32);
620 img.fill(PREMUL(color.rgba()));
621 }
622 return img;
623}
624
625extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha);
626
627QImage QGLPixmapData::toImage() const
628{
629 if (!isValid())
630 return QImage();
631
632 if (m_renderFbo) {
633 copyBackFromRenderFbo(true);
634 } else if (!m_source.isNull()) {
635 QImageData *data = const_cast<QImage &>(m_source).data_ptr();
636 if (data->paintEngine && data->paintEngine->isActive()
637 && data->paintEngine->paintDevice() == &m_source)
638 {
639 return m_source.copy();
640 }
641 return m_source;
642 } else if (m_dirty || m_hasFillColor) {
643 return fillImage(m_fillColor);
644 } else {
645 ensureCreated();
646 }
647
648 QGLShareContextScope ctx(qt_gl_share_context());
649 glBindTexture(GL_TEXTURE_2D, m_texture.id);
650 return qt_gl_read_texture(QSize(w, h), true, true);
651}
652
653struct TextureBuffer
654{
655 QGLFramebufferObject *fbo;
656 QGL2PaintEngineEx *engine;
657};
658
659Q_GLOBAL_STATIC(QGLFramebufferObjectPool, _qgl_fbo_pool)
660QGLFramebufferObjectPool* qgl_fbo_pool()
661{
662 return _qgl_fbo_pool();
663}
664
665void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const
666{
667 if (!isValid())
668 return;
669
670 m_hasFillColor = false;
671
672 const QGLContext *share_ctx = qt_gl_share_context();
673 QGLShareContextScope ctx(share_ctx);
674
675 ensureCreated();
676
677 if (!ctx->d_ptr->fbo)
678 glGenFramebuffers(1, &ctx->d_ptr->fbo);
679
680 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
681 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
682 GL_TEXTURE_2D, m_texture.id, 0);
683
684 const int x0 = 0;
685 const int x1 = w;
686 const int y0 = 0;
687 const int y1 = h;
688
689 if (!m_renderFbo->isBound())
690 glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, m_renderFbo->handle());
691
692 glDisable(GL_SCISSOR_TEST);
693
694 glBlitFramebufferEXT(x0, y0, x1, y1,
695 x0, y0, x1, y1,
696 GL_COLOR_BUFFER_BIT,
697 GL_NEAREST);
698
699 if (keepCurrentFboBound) {
700 glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo);
701 } else {
702 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, m_renderFbo->handle());
703 ctx->d_ptr->current_fbo = m_renderFbo->handle();
704 }
705}
706
707bool QGLPixmapData::useFramebufferObjects() const
708{
709 return QGLFramebufferObject::hasOpenGLFramebufferObjects()
710 && QGLFramebufferObject::hasOpenGLFramebufferBlit()
711 && qt_gl_preferGL2Engine()
712 && (w * h > 32*32); // avoid overhead of FBOs for small pixmaps
713}
714
715QPaintEngine* QGLPixmapData::paintEngine() const
716{
717 if (!isValid())
718 return 0;
719
720 if (m_renderFbo)
721 return m_engine;
722
723 if (useFramebufferObjects()) {
724 extern QGLWidget* qt_gl_share_widget();
725
726 if (!QGLContext::currentContext())
727 const_cast<QGLContext *>(qt_gl_share_context())->makeCurrent();
728 QGLShareContextScope ctx(qt_gl_share_context());
729
730 QGLFramebufferObjectFormat format;
731 format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
732 format.setSamples(4);
733 format.setInternalTextureFormat(GLenum(m_hasAlpha ? GL_RGBA : GL_RGB));
734
735 m_renderFbo = qgl_fbo_pool()->acquire(size(), format);
736
737 if (m_renderFbo) {
738 if (!m_engine)
739 m_engine = new QGL2PaintEngineEx;
740 return m_engine;
741 }
742
743 qWarning() << "Failed to create pixmap texture buffer of size " << size() << ", falling back to raster paint engine";
744 }
745
746 m_dirty = true;
747 if (m_source.size() != size())
748 m_source = QImage(size(), QImage::Format_ARGB32_Premultiplied);
749 if (m_hasFillColor) {
750 m_source.fill(PREMUL(m_fillColor.rgba()));
751 m_hasFillColor = false;
752 }
753 return m_source.paintEngine();
754}
755
756extern QRgb qt_gl_convertToGLFormat(QRgb src_pixel, GLenum texture_format);
757
758// If copyBack is true, bind will copy the contents of the render
759// FBO to the texture (which is not bound to the texture, as it's
760// a multisample FBO).
761GLuint QGLPixmapData::bind(bool copyBack) const
762{
763 if (m_renderFbo && copyBack) {
764 copyBackFromRenderFbo(true);
765 } else {
766 ensureCreated();
767 }
768
769 GLuint id = m_texture.id;
770 glBindTexture(GL_TEXTURE_2D, id);
771
772 if (m_hasFillColor) {
773 if (!useFramebufferObjects()) {
774 m_source = QImage(w, h, QImage::Format_ARGB32_Premultiplied);
775 m_source.fill(PREMUL(m_fillColor.rgba()));
776 }
777
778 m_hasFillColor = false;
779
780 GLenum format = qt_gl_preferredTextureFormat();
781 QImage tx(w, h, QImage::Format_ARGB32_Premultiplied);
782 tx.fill(qt_gl_convertToGLFormat(m_fillColor.rgba(), format));
783 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, tx.bits());
784 }
785
786 return id;
787}
788
789QGLTexture* QGLPixmapData::texture() const
790{
791 return &m_texture;
792}
793
794int QGLPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
795{
796 if (w == 0)
797 return 0;
798
799 switch (metric) {
800 case QPaintDevice::PdmWidth:
801 return w;
802 case QPaintDevice::PdmHeight:
803 return h;
804 case QPaintDevice::PdmNumColors:
805 return 0;
806 case QPaintDevice::PdmDepth:
807 return d;
808 case QPaintDevice::PdmWidthMM:
809 return qRound(w * 25.4 / qt_defaultDpiX());
810 case QPaintDevice::PdmHeightMM:
811 return qRound(h * 25.4 / qt_defaultDpiY());
812 case QPaintDevice::PdmDpiX:
813 case QPaintDevice::PdmPhysicalDpiX:
814 return qt_defaultDpiX();
815 case QPaintDevice::PdmDpiY:
816 case QPaintDevice::PdmPhysicalDpiY:
817 return qt_defaultDpiY();
818 default:
819 qWarning("QGLPixmapData::metric(): Invalid metric");
820 return 0;
821 }
822}
823
824QGLPaintDevice *QGLPixmapData::glDevice() const
825{
826 return &m_glDevice;
827}
828
829QT_END_NAMESPACE
830