Coverage Report

Created: 2025-09-27 07:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/painting/qplatformbackingstore.cpp
Line
Count
Source
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:
88
    QPlatformBackingStorePrivate(QWindow *w)
89
0
        : window(w)
90
0
        , backingStore(nullptr)
91
#ifndef QT_NO_OPENGL
92
        , textureId(0)
93
        , blitter(nullptr)
94
#endif
95
0
    {
96
0
    }
97
98
    ~QPlatformBackingStorePrivate()
99
0
    {
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
0
    }
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:
142
    QPlatformTextureListPrivate()
143
        : locked(false)
144
    {
145
    }
146
147
    QVector<QBackingstoreTextureInfo> textures;
148
    bool locked;
149
};
150
151
QPlatformTextureList::QPlatformTextureList(QObject *parent)
152
: QObject(*new QPlatformTextureListPrivate, parent)
153
{
154
}
155
156
QPlatformTextureList::~QPlatformTextureList()
157
{
158
}
159
160
int QPlatformTextureList::count() const
161
{
162
    Q_D(const QPlatformTextureList);
163
    return d->textures.count();
164
}
165
166
GLuint QPlatformTextureList::textureId(int index) const
167
{
168
    Q_D(const QPlatformTextureList);
169
    return d->textures.at(index).textureId;
170
}
171
172
void *QPlatformTextureList::source(int index)
173
{
174
    Q_D(const QPlatformTextureList);
175
    return d->textures.at(index).source;
176
}
177
178
QPlatformTextureList::Flags QPlatformTextureList::flags(int index) const
179
{
180
    Q_D(const QPlatformTextureList);
181
    return d->textures.at(index).flags;
182
}
183
184
QRect QPlatformTextureList::geometry(int index) const
185
{
186
    Q_D(const QPlatformTextureList);
187
    return d->textures.at(index).rect;
188
}
189
190
QRect QPlatformTextureList::clipRect(int index) const
191
{
192
    Q_D(const QPlatformTextureList);
193
    return d->textures.at(index).clipRect;
194
}
195
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
205
bool QPlatformTextureList::isLocked() const
206
{
207
    Q_D(const QPlatformTextureList);
208
    return d->locked;
209
}
210
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
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
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
251
static inline QPoint deviceOffset(const QPoint &pt, QWindow *window)
252
{
253
    return pt * window->devicePixelRatio();
254
}
255
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
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
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
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
 */
491
QImage QPlatformBackingStore::toImage() const
492
0
{
493
0
    return QImage();
494
0
}
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
 */
524
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
*/
670
QPlatformBackingStore::QPlatformBackingStore(QWindow *window)
671
0
    : d_ptr(new QPlatformBackingStorePrivate(window))
672
0
{
673
0
}
674
675
/*!
676
    Destroys this surface.
677
*/
678
QPlatformBackingStore::~QPlatformBackingStore()
679
0
{
680
0
    delete d_ptr;
681
0
}
682
683
/*!
684
    Returns a pointer to the top-level window associated with this
685
    surface.
686
*/
687
QWindow* QPlatformBackingStore::window() const
688
0
{
689
0
    return d_ptr->window;
690
0
}
691
692
/*!
693
    Sets the backing store associated with this surface.
694
*/
695
void QPlatformBackingStore::setBackingStore(QBackingStore *backingStore)
696
0
{
697
0
    d_ptr->backingStore = backingStore;
698
0
}
699
700
/*!
701
    Returns a pointer to the backing store associated with this
702
    surface.
703
*/
704
QBackingStore *QPlatformBackingStore::backingStore() const
705
0
{
706
0
    return d_ptr->backingStore;
707
0
}
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
716
void QPlatformBackingStore::beginPaint(const QRegion &)
717
0
{
718
0
}
719
720
/*!
721
    This function is called after painting onto the surface has ended.
722
723
    \sa beginPaint(), paintDevice()
724
*/
725
726
void QPlatformBackingStore::endPaint()
727
0
{
728
0
}
729
730
/*!
731
    Accessor for a backingstores graphics buffer abstraction
732
*/
733
QPlatformGraphicsBuffer *QPlatformBackingStore::graphicsBuffer() const
734
0
{
735
0
    return nullptr;
736
0
}
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
*/
744
bool QPlatformBackingStore::scroll(const QRegion &area, int dx, int dy)
745
0
{
746
0
    Q_UNUSED(area);
747
0
    Q_UNUSED(dx);
748
0
    Q_UNUSED(dy);
749
750
0
    return false;
751
0
}
752
753
QT_END_NAMESPACE
754
755
#include "moc_qplatformbackingstore.cpp"