1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui 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 The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qplatformbackingstore.h"
41 #include <qwindow.h>
42 #include <qpixmap.h>
43 #include <private/qwindow_p.h>
44 
45 #include <qopengl.h>
46 #include <qopenglcontext.h>
47 #include <QtGui/QMatrix4x4>
48 #include <QtGui/QOpenGLShaderProgram>
49 #include <QtGui/QOpenGLContext>
50 #include <QtGui/QOpenGLFunctions>
51 #ifndef QT_NO_OPENGL
52 #include <QtGui/qopengltextureblitter.h>
53 #include <QtGui/qoffscreensurface.h>
54 #endif
55 #include <qpa/qplatformgraphicsbuffer.h>
56 #include <qpa/qplatformgraphicsbufferhelper.h>
57 
58 #ifndef GL_TEXTURE_BASE_LEVEL
59 #define GL_TEXTURE_BASE_LEVEL             0x813C
60 #endif
61 #ifndef GL_TEXTURE_MAX_LEVEL
62 #define GL_TEXTURE_MAX_LEVEL              0x813D
63 #endif
64 #ifndef GL_UNPACK_ROW_LENGTH
65 #define GL_UNPACK_ROW_LENGTH              0x0CF2
66 #endif
67 #ifndef GL_RGB10_A2
68 #define GL_RGB10_A2                       0x8059
69 #endif
70 #ifndef GL_UNSIGNED_INT_2_10_10_10_REV
71 #define GL_UNSIGNED_INT_2_10_10_10_REV    0x8368
72 #endif
73 
74 #ifndef GL_FRAMEBUFFER_SRGB
75 #define GL_FRAMEBUFFER_SRGB 0x8DB9
76 #endif
77 #ifndef GL_FRAMEBUFFER_SRGB_CAPABLE
78 #define GL_FRAMEBUFFER_SRGB_CAPABLE 0x8DBA
79 #endif
80 
81 QT_BEGIN_NAMESPACE
82 
83 Q_LOGGING_CATEGORY(lcQpaBackingStore, "qt.qpa.backingstore", QtWarningMsg);
84 
85 class QPlatformBackingStorePrivate
86 {
87 public:
QPlatformBackingStorePrivate(QWindow * w)88     QPlatformBackingStorePrivate(QWindow *w)
89         : window(w)
90         , backingStore(nullptr)
91 #ifndef QT_NO_OPENGL
92         , textureId(0)
93         , blitter(nullptr)
94 #endif
95     {
96     }
97 
~QPlatformBackingStorePrivate()98     ~QPlatformBackingStorePrivate()
99     {
100 #ifndef QT_NO_OPENGL
101         if (context) {
102             QOffscreenSurface offscreenSurface;
103             offscreenSurface.setFormat(context->format());
104             offscreenSurface.create();
105             context->makeCurrent(&offscreenSurface);
106             if (textureId)
107                 context->functions()->glDeleteTextures(1, &textureId);
108             if (blitter)
109                 blitter->destroy();
110         }
111         delete blitter;
112 #endif
113     }
114     QWindow *window;
115     QBackingStore *backingStore;
116 #ifndef QT_NO_OPENGL
117     QScopedPointer<QOpenGLContext> context;
118     mutable GLuint textureId;
119     mutable QSize textureSize;
120     mutable bool needsSwizzle;
121     mutable bool premultiplied;
122     QOpenGLTextureBlitter *blitter;
123 #endif
124 };
125 
126 #ifndef QT_NO_OPENGL
127 
128 struct QBackingstoreTextureInfo
129 {
130     void *source; // may be null
131     GLuint textureId;
132     QRect rect;
133     QRect clipRect;
134     QPlatformTextureList::Flags flags;
135 };
136 
137 Q_DECLARE_TYPEINFO(QBackingstoreTextureInfo, Q_MOVABLE_TYPE);
138 
139 class QPlatformTextureListPrivate : public QObjectPrivate
140 {
141 public:
QPlatformTextureListPrivate()142     QPlatformTextureListPrivate()
143         : locked(false)
144     {
145     }
146 
147     QVector<QBackingstoreTextureInfo> textures;
148     bool locked;
149 };
150 
QPlatformTextureList(QObject * parent)151 QPlatformTextureList::QPlatformTextureList(QObject *parent)
152 : QObject(*new QPlatformTextureListPrivate, parent)
153 {
154 }
155 
~QPlatformTextureList()156 QPlatformTextureList::~QPlatformTextureList()
157 {
158 }
159 
count() const160 int QPlatformTextureList::count() const
161 {
162     Q_D(const QPlatformTextureList);
163     return d->textures.count();
164 }
165 
textureId(int index) const166 GLuint QPlatformTextureList::textureId(int index) const
167 {
168     Q_D(const QPlatformTextureList);
169     return d->textures.at(index).textureId;
170 }
171 
source(int index)172 void *QPlatformTextureList::source(int index)
173 {
174     Q_D(const QPlatformTextureList);
175     return d->textures.at(index).source;
176 }
177 
flags(int index) const178 QPlatformTextureList::Flags QPlatformTextureList::flags(int index) const
179 {
180     Q_D(const QPlatformTextureList);
181     return d->textures.at(index).flags;
182 }
183 
geometry(int index) const184 QRect QPlatformTextureList::geometry(int index) const
185 {
186     Q_D(const QPlatformTextureList);
187     return d->textures.at(index).rect;
188 }
189 
clipRect(int index) const190 QRect QPlatformTextureList::clipRect(int index) const
191 {
192     Q_D(const QPlatformTextureList);
193     return d->textures.at(index).clipRect;
194 }
195 
lock(bool on)196 void QPlatformTextureList::lock(bool on)
197 {
198     Q_D(QPlatformTextureList);
199     if (on != d->locked) {
200         d->locked = on;
201         emit locked(on);
202     }
203 }
204 
isLocked() const205 bool QPlatformTextureList::isLocked() const
206 {
207     Q_D(const QPlatformTextureList);
208     return d->locked;
209 }
210 
appendTexture(void * source,GLuint textureId,const QRect & geometry,const QRect & clipRect,Flags flags)211 void QPlatformTextureList::appendTexture(void *source, GLuint textureId, const QRect &geometry,
212                                          const QRect &clipRect, Flags flags)
213 {
214     Q_D(QPlatformTextureList);
215     QBackingstoreTextureInfo bi;
216     bi.source = source;
217     bi.textureId = textureId;
218     bi.rect = geometry;
219     bi.clipRect = clipRect;
220     bi.flags = flags;
221     d->textures.append(bi);
222 }
223 
clear()224 void QPlatformTextureList::clear()
225 {
226     Q_D(QPlatformTextureList);
227     d->textures.clear();
228 }
229 #endif // QT_NO_OPENGL
230 
231 /*!
232     \class QPlatformBackingStore
233     \since 5.0
234     \internal
235     \preliminary
236     \ingroup qpa
237 
238     \brief The QPlatformBackingStore class provides the drawing area for top-level
239     windows.
240 */
241 
242 #ifndef QT_NO_OPENGL
243 
deviceRect(const QRect & rect,QWindow * window)244 static inline QRect deviceRect(const QRect &rect, QWindow *window)
245 {
246     QRect deviceRect(rect.topLeft() * window->devicePixelRatio(),
247                      rect.size() * window->devicePixelRatio());
248     return deviceRect;
249 }
250 
deviceOffset(const QPoint & pt,QWindow * window)251 static inline QPoint deviceOffset(const QPoint &pt, QWindow *window)
252 {
253     return pt * window->devicePixelRatio();
254 }
255 
deviceRegion(const QRegion & region,QWindow * window,const QPoint & offset)256 static QRegion deviceRegion(const QRegion &region, QWindow *window, const QPoint &offset)
257 {
258     if (offset.isNull() && window->devicePixelRatio() <= 1)
259         return region;
260 
261     QVector<QRect> rects;
262     rects.reserve(region.rectCount());
263     for (const QRect &rect : region)
264         rects.append(deviceRect(rect.translated(offset), window));
265 
266     QRegion deviceRegion;
267     deviceRegion.setRects(rects.constData(), rects.count());
268     return deviceRegion;
269 }
270 
toBottomLeftRect(const QRect & topLeftRect,int windowHeight)271 static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight)
272 {
273     return QRect(topLeftRect.x(), windowHeight - topLeftRect.bottomRight().y() - 1,
274                  topLeftRect.width(), topLeftRect.height());
275 }
276 
blitTextureForWidget(const QPlatformTextureList * textures,int idx,QWindow * window,const QRect & deviceWindowRect,QOpenGLTextureBlitter * blitter,const QPoint & offset,bool canUseSrgb)277 static void blitTextureForWidget(const QPlatformTextureList *textures, int idx, QWindow *window, const QRect &deviceWindowRect,
278                                  QOpenGLTextureBlitter *blitter, const QPoint &offset, bool canUseSrgb)
279 {
280     const QRect clipRect = textures->clipRect(idx);
281     if (clipRect.isEmpty())
282         return;
283 
284     QRect rectInWindow = textures->geometry(idx);
285     // relative to the TLW, not necessarily our window (if the flush is for a native child widget), have to adjust
286     rectInWindow.translate(-offset);
287 
288     const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft());
289     const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height());
290 
291     const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(deviceRect(clippedRectInWindow, window),
292                                                                      deviceWindowRect);
293 
294     const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(deviceRect(srcRect, window),
295                                                                      deviceRect(rectInWindow, window).size(),
296                                                                      QOpenGLTextureBlitter::OriginBottomLeft);
297 
298     QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
299     const bool srgb = textures->flags(idx).testFlag(QPlatformTextureList::TextureIsSrgb);
300     if (srgb && canUseSrgb)
301         funcs->glEnable(GL_FRAMEBUFFER_SRGB);
302 
303     blitter->blit(textures->textureId(idx), target, source);
304 
305     if (srgb && canUseSrgb)
306         funcs->glDisable(GL_FRAMEBUFFER_SRGB);
307 }
308 
309 /*!
310     Flushes the given \a region from the specified \a window onto the
311     screen, and composes it with the specified \a textures.
312 
313     The default implementation retrieves the contents using toTexture()
314     and composes using OpenGL. May be reimplemented in subclasses if there
315     is a more efficient native way to do it.
316 
317     \note \a region is relative to the window which may not be top-level in case
318     \a window corresponds to a native child widget. \a offset is the position of
319     the native child relative to the top-level window.
320  */
321 
composeAndFlush(QWindow * window,const QRegion & region,const QPoint & offset,QPlatformTextureList * textures,bool translucentBackground)322 void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion &region,
323                                             const QPoint &offset,
324                                             QPlatformTextureList *textures,
325                                             bool translucentBackground)
326 {
327     if (!qt_window_private(window)->receivedExpose)
328         return;
329 
330     if (!d_ptr->context) {
331         d_ptr->context.reset(new QOpenGLContext);
332         d_ptr->context->setFormat(d_ptr->window->requestedFormat());
333         d_ptr->context->setScreen(d_ptr->window->screen());
334         d_ptr->context->setShareContext(qt_window_private(d_ptr->window)->shareContext());
335         if (!d_ptr->context->create()) {
336             qCWarning(lcQpaBackingStore, "composeAndFlush: QOpenGLContext creation failed");
337             return;
338         }
339     }
340 
341     bool current = d_ptr->context->makeCurrent(window);
342 
343     if (!current && !d_ptr->context->isValid()) {
344         delete d_ptr->blitter;
345         d_ptr->blitter = nullptr;
346         d_ptr->textureId = 0;
347         current = d_ptr->context->create() && d_ptr->context->makeCurrent(window);
348     }
349 
350     if (!current) {
351         qCWarning(lcQpaBackingStore, "composeAndFlush: makeCurrent() failed");
352         return;
353     }
354 
355     qCDebug(lcQpaBackingStore) << "Composing and flushing" << region << "of" << window
356         << "at offset" << offset << "with" << textures->count() << "texture(s) in" << textures;
357 
358     QWindowPrivate::get(window)->lastComposeTime.start();
359 
360     QOpenGLFunctions *funcs = d_ptr->context->functions();
361     funcs->glViewport(0, 0, qRound(window->width() * window->devicePixelRatio()), qRound(window->height() * window->devicePixelRatio()));
362     funcs->glClearColor(0, 0, 0, translucentBackground ? 0 : 1);
363     funcs->glClear(GL_COLOR_BUFFER_BIT);
364 
365     if (!d_ptr->blitter) {
366         d_ptr->blitter = new QOpenGLTextureBlitter;
367         d_ptr->blitter->create();
368     }
369 
370     d_ptr->blitter->bind();
371 
372     const QRect deviceWindowRect = deviceRect(QRect(QPoint(), window->size()), window);
373     const QPoint deviceWindowOffset = deviceOffset(offset, window);
374 
375     bool canUseSrgb = false;
376     // If there are any sRGB textures in the list, check if the destination
377     // framebuffer is sRGB capable.
378     for (int i = 0; i < textures->count(); ++i) {
379         if (textures->flags(i).testFlag(QPlatformTextureList::TextureIsSrgb)) {
380             GLint cap = 0;
381             funcs->glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE, &cap);
382             if (cap)
383                 canUseSrgb = true;
384             break;
385         }
386     }
387 
388     // Textures for renderToTexture widgets.
389     for (int i = 0; i < textures->count(); ++i) {
390         if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop))
391             blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset, canUseSrgb);
392     }
393 
394     // Backingstore texture with the normal widgets.
395     GLuint textureId = 0;
396     QOpenGLTextureBlitter::Origin origin = QOpenGLTextureBlitter::OriginTopLeft;
397     if (QPlatformGraphicsBuffer *graphicsBuffer = this->graphicsBuffer()) {
398         if (graphicsBuffer->size() != d_ptr->textureSize) {
399             if (d_ptr->textureId)
400                 funcs->glDeleteTextures(1, &d_ptr->textureId);
401             funcs->glGenTextures(1, &d_ptr->textureId);
402             funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId);
403             QOpenGLContext *ctx = QOpenGLContext::currentContext();
404             if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
405                 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
406                 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
407             }
408             funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
409             funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
410             funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
411             funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
412 
413             if (QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &d_ptr->needsSwizzle, &d_ptr->premultiplied)) {
414                 d_ptr->textureSize = graphicsBuffer->size();
415             } else {
416                 d_ptr->textureSize = QSize(0,0);
417             }
418 
419             graphicsBuffer->unlock();
420         } else if (!region.isEmpty()){
421             funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId);
422             QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &d_ptr->needsSwizzle, &d_ptr->premultiplied);
423             graphicsBuffer->unlock();
424         }
425 
426         if (graphicsBuffer->origin() == QPlatformGraphicsBuffer::OriginBottomLeft)
427             origin = QOpenGLTextureBlitter::OriginBottomLeft;
428         textureId = d_ptr->textureId;
429     } else {
430         TextureFlags flags;
431         textureId = toTexture(deviceRegion(region, window, offset), &d_ptr->textureSize, &flags);
432         d_ptr->needsSwizzle = (flags & TextureSwizzle) != 0;
433         d_ptr->premultiplied = (flags & TexturePremultiplied) != 0;
434         if (flags & TextureFlip)
435             origin = QOpenGLTextureBlitter::OriginBottomLeft;
436     }
437 
438     funcs->glEnable(GL_BLEND);
439     if (d_ptr->premultiplied)
440         funcs->glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
441     else
442         funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
443 
444     if (textureId) {
445         if (d_ptr->needsSwizzle)
446             d_ptr->blitter->setRedBlueSwizzle(true);
447         // The backingstore is for the entire tlw.
448         // In case of native children offset tells the position relative to the tlw.
449         const QRect srcRect = toBottomLeftRect(deviceWindowRect.translated(deviceWindowOffset), d_ptr->textureSize.height());
450         const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect,
451                                                                          d_ptr->textureSize,
452                                                                          origin);
453         d_ptr->blitter->blit(textureId, QMatrix4x4(), source);
454         if (d_ptr->needsSwizzle)
455             d_ptr->blitter->setRedBlueSwizzle(false);
456     }
457 
458     // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set.
459     bool blendIsPremultiplied = d_ptr->premultiplied;
460     for (int i = 0; i < textures->count(); ++i) {
461         const QPlatformTextureList::Flags flags = textures->flags(i);
462         if (flags.testFlag(QPlatformTextureList::NeedsPremultipliedAlphaBlending)) {
463             if (!blendIsPremultiplied) {
464                 funcs->glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
465                 blendIsPremultiplied = true;
466             }
467         } else {
468             if (blendIsPremultiplied) {
469                 funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
470                 blendIsPremultiplied = false;
471             }
472         }
473         if (flags.testFlag(QPlatformTextureList::StacksOnTop))
474             blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset, canUseSrgb);
475     }
476 
477     funcs->glDisable(GL_BLEND);
478     d_ptr->blitter->release();
479 
480     d_ptr->context->swapBuffers(window);
481 }
482 #endif
483 /*!
484   Implemented in subclasses to return the content of the backingstore as a QImage.
485 
486   If QPlatformIntegration::RasterGLSurface is supported, either this function or
487   toTexture() must be implemented.
488 
489   \sa toTexture()
490  */
toImage() const491 QImage QPlatformBackingStore::toImage() const
492 {
493     return QImage();
494 }
495 #ifndef QT_NO_OPENGL
496 /*!
497   May be reimplemented in subclasses to return the content of the
498   backingstore as an OpenGL texture. \a dirtyRegion is the part of the
499   backingstore which may have changed since the last call to this function. The
500   caller of this function must ensure that there is a current context.
501 
502   The size of the texture is returned in \a textureSize.
503 
504   The ownership of the texture is not transferred. The caller must not store
505   the return value between calls, but instead call this function before each use.
506 
507   The default implementation returns a cached texture if \a dirtyRegion is empty and
508   \a textureSize matches the backingstore size, otherwise it retrieves the content using
509   toImage() and performs a texture upload. This works only if the value of \a textureSize
510   is preserved between the calls to this function.
511 
512   If the red and blue components have to swapped, \a flags will be set to include \c
513   TextureSwizzle. This allows creating textures from images in formats like
514   QImage::Format_RGB32 without any further image conversion. Instead, the swizzling will
515   be done in the shaders when performing composition. Other formats, that do not need
516   such swizzling due to being already byte ordered RGBA, for example
517   QImage::Format_RGBA8888, must result in having \a needsSwizzle set to false.
518 
519   If the image has to be flipped (e.g. because the texture is attached to an FBO), \a
520   flags will be set to include \c TextureFlip.
521 
522   \note \a dirtyRegion is relative to the backingstore so no adjustment is needed.
523  */
toTexture(const QRegion & dirtyRegion,QSize * textureSize,TextureFlags * flags) const524 GLuint QPlatformBackingStore::toTexture(const QRegion &dirtyRegion, QSize *textureSize, TextureFlags *flags) const
525 {
526     Q_ASSERT(textureSize);
527     Q_ASSERT(flags);
528 
529     QImage image = toImage();
530     QSize imageSize = image.size();
531 
532     QOpenGLContext *ctx = QOpenGLContext::currentContext();
533     GLenum internalFormat = GL_RGBA;
534     GLuint pixelType = GL_UNSIGNED_BYTE;
535 
536     bool needsConversion = false;
537     *flags = { };
538     switch (image.format()) {
539     case QImage::Format_ARGB32_Premultiplied:
540         *flags |= TexturePremultiplied;
541         Q_FALLTHROUGH();
542     case QImage::Format_RGB32:
543     case QImage::Format_ARGB32:
544         *flags |= TextureSwizzle;
545         break;
546     case QImage::Format_RGBA8888_Premultiplied:
547         *flags |= TexturePremultiplied;
548         Q_FALLTHROUGH();
549     case QImage::Format_RGBX8888:
550     case QImage::Format_RGBA8888:
551         break;
552     case QImage::Format_BGR30:
553     case QImage::Format_A2BGR30_Premultiplied:
554         if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
555             pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
556             internalFormat = GL_RGB10_A2;
557             *flags |= TexturePremultiplied;
558         } else {
559             needsConversion = true;
560         }
561         break;
562     case QImage::Format_RGB30:
563     case QImage::Format_A2RGB30_Premultiplied:
564         if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
565             pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
566             internalFormat = GL_RGB10_A2;
567             *flags |= TextureSwizzle | TexturePremultiplied;
568         } else {
569             needsConversion = true;
570         }
571         break;
572     default:
573         needsConversion = true;
574         break;
575     }
576     if (imageSize.isEmpty()) {
577         *textureSize = imageSize;
578         return 0;
579     }
580 
581     // Must rely on the input only, not d_ptr.
582     // With the default composeAndFlush() textureSize is &d_ptr->textureSize.
583     bool resized = *textureSize != imageSize;
584     if (dirtyRegion.isEmpty() && !resized)
585         return d_ptr->textureId;
586 
587     *textureSize = imageSize;
588 
589     if (needsConversion)
590         image = image.convertToFormat(QImage::Format_RGBA8888);
591 
592     // The image provided by the backingstore may have a stride larger than width * 4, for
593     // instance on platforms that manually implement client-side decorations.
594     static const int bytesPerPixel = 4;
595     const int strideInPixels = image.bytesPerLine() / bytesPerPixel;
596     const bool hasUnpackRowLength = !ctx->isOpenGLES() || ctx->format().majorVersion() >= 3;
597 
598     QOpenGLFunctions *funcs = ctx->functions();
599 
600     if (hasUnpackRowLength) {
601         funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, strideInPixels);
602     } else if (strideInPixels != image.width()) {
603         // No UNPACK_ROW_LENGTH on ES 2.0 and yet we would need it. This case is typically
604         // hit with QtWayland which is rarely used in combination with a ES2.0-only GL
605         // implementation.  Therefore, accept the performance hit and do a copy.
606         image = image.copy();
607     }
608 
609     if (resized) {
610         if (d_ptr->textureId)
611             funcs->glDeleteTextures(1, &d_ptr->textureId);
612         funcs->glGenTextures(1, &d_ptr->textureId);
613         funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId);
614         if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
615             funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
616             funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
617         }
618         funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
619         funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
620         funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
621         funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
622 
623         funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, imageSize.width(), imageSize.height(), 0, GL_RGBA, pixelType,
624                             const_cast<uchar*>(image.constBits()));
625     } else {
626         funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId);
627         QRect imageRect = image.rect();
628         QRect rect = dirtyRegion.boundingRect() & imageRect;
629 
630         if (hasUnpackRowLength) {
631             funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
632                                    image.constScanLine(rect.y()) + rect.x() * bytesPerPixel);
633         } else {
634             // if the rect is wide enough it's cheaper to just
635             // extend it instead of doing an image copy
636             if (rect.width() >= imageRect.width() / 2) {
637                 rect.setX(0);
638                 rect.setWidth(imageRect.width());
639             }
640 
641             // if the sub-rect is full-width we can pass the image data directly to
642             // OpenGL instead of copying, since there's no gap between scanlines
643 
644             if (rect.width() == imageRect.width()) {
645                 funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
646                                        image.constScanLine(rect.y()));
647             } else {
648                 funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
649                                        image.copy(rect).constBits());
650             }
651         }
652     }
653 
654     if (hasUnpackRowLength)
655         funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
656 
657     return d_ptr->textureId;
658 }
659 #endif // QT_NO_OPENGL
660 
661 /*!
662     \fn QPaintDevice* QPlatformBackingStore::paintDevice()
663 
664     Implement this function to return the appropriate paint device.
665 */
666 
667 /*!
668     Constructs an empty surface for the given top-level \a window.
669 */
QPlatformBackingStore(QWindow * window)670 QPlatformBackingStore::QPlatformBackingStore(QWindow *window)
671     : d_ptr(new QPlatformBackingStorePrivate(window))
672 {
673 }
674 
675 /*!
676     Destroys this surface.
677 */
~QPlatformBackingStore()678 QPlatformBackingStore::~QPlatformBackingStore()
679 {
680     delete d_ptr;
681 }
682 
683 /*!
684     Returns a pointer to the top-level window associated with this
685     surface.
686 */
window() const687 QWindow* QPlatformBackingStore::window() const
688 {
689     return d_ptr->window;
690 }
691 
692 /*!
693     Sets the backing store associated with this surface.
694 */
setBackingStore(QBackingStore * backingStore)695 void QPlatformBackingStore::setBackingStore(QBackingStore *backingStore)
696 {
697     d_ptr->backingStore = backingStore;
698 }
699 
700 /*!
701     Returns a pointer to the backing store associated with this
702     surface.
703 */
backingStore() const704 QBackingStore *QPlatformBackingStore::backingStore() const
705 {
706     return d_ptr->backingStore;
707 }
708 
709 /*!
710     This function is called before painting onto the surface begins,
711     with the \a region in which the painting will occur.
712 
713     \sa endPaint(), paintDevice()
714 */
715 
beginPaint(const QRegion &)716 void QPlatformBackingStore::beginPaint(const QRegion &)
717 {
718 }
719 
720 /*!
721     This function is called after painting onto the surface has ended.
722 
723     \sa beginPaint(), paintDevice()
724 */
725 
endPaint()726 void QPlatformBackingStore::endPaint()
727 {
728 }
729 
730 /*!
731     Accessor for a backingstores graphics buffer abstraction
732 */
graphicsBuffer() const733 QPlatformGraphicsBuffer *QPlatformBackingStore::graphicsBuffer() const
734 {
735     return nullptr;
736 }
737 
738 /*!
739     Scrolls the given \a area \a dx pixels to the right and \a dy
740     downward; both \a dx and \a dy may be negative.
741 
742     Returns \c true if the area was scrolled successfully; false otherwise.
743 */
scroll(const QRegion & area,int dx,int dy)744 bool QPlatformBackingStore::scroll(const QRegion &area, int dx, int dy)
745 {
746     Q_UNUSED(area);
747     Q_UNUSED(dx);
748     Q_UNUSED(dy);
749 
750     return false;
751 }
752 
753 QT_END_NAMESPACE
754