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
81QT_BEGIN_NAMESPACE
82
83Q_LOGGING_CATEGORY(lcQpaBackingStore, "qt.qpa.backingstore", QtWarningMsg);
84
85class QPlatformBackingStorePrivate
86{
87public:
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
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(surface: &offscreenSurface);
106 if (textureId)
107 context->functions()->glDeleteTextures(n: 1, textures: &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
128struct QBackingstoreTextureInfo
129{
130 void *source; // may be null
131 GLuint textureId;
132 QRect rect;
133 QRect clipRect;
134 QPlatformTextureList::Flags flags;
135};
136
137Q_DECLARE_TYPEINFO(QBackingstoreTextureInfo, Q_MOVABLE_TYPE);
138
139class QPlatformTextureListPrivate : public QObjectPrivate
140{
141public:
142 QPlatformTextureListPrivate()
143 : locked(false)
144 {
145 }
146
147 QVector<QBackingstoreTextureInfo> textures;
148 bool locked;
149};
150
151QPlatformTextureList::QPlatformTextureList(QObject *parent)
152: QObject(*new QPlatformTextureListPrivate, parent)
153{
154}
155
156QPlatformTextureList::~QPlatformTextureList()
157{
158}
159
160int QPlatformTextureList::count() const
161{
162 Q_D(const QPlatformTextureList);
163 return d->textures.count();
164}
165
166GLuint QPlatformTextureList::textureId(int index) const
167{
168 Q_D(const QPlatformTextureList);
169 return d->textures.at(i: index).textureId;
170}
171
172void *QPlatformTextureList::source(int index)
173{
174 Q_D(const QPlatformTextureList);
175 return d->textures.at(i: index).source;
176}
177
178QPlatformTextureList::Flags QPlatformTextureList::flags(int index) const
179{
180 Q_D(const QPlatformTextureList);
181 return d->textures.at(i: index).flags;
182}
183
184QRect QPlatformTextureList::geometry(int index) const
185{
186 Q_D(const QPlatformTextureList);
187 return d->textures.at(i: index).rect;
188}
189
190QRect QPlatformTextureList::clipRect(int index) const
191{
192 Q_D(const QPlatformTextureList);
193 return d->textures.at(i: index).clipRect;
194}
195
196void 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
205bool QPlatformTextureList::isLocked() const
206{
207 Q_D(const QPlatformTextureList);
208 return d->locked;
209}
210
211void 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(t: bi);
222}
223
224void 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
244static 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
251static inline QPoint deviceOffset(const QPoint &pt, QWindow *window)
252{
253 return pt * window->devicePixelRatio();
254}
255
256static 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(asize: region.rectCount());
263 for (const QRect &rect : region)
264 rects.append(t: deviceRect(rect: rect.translated(p: offset), window));
265
266 QRegion deviceRegion;
267 deviceRegion.setRects(rect: rects.constData(), num: rects.count());
268 return deviceRegion;
269}
270
271static 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
277static 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(index: idx);
281 if (clipRect.isEmpty())
282 return;
283
284 QRect rectInWindow = textures->geometry(index: 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(p: -offset);
287
288 const QRect clippedRectInWindow = rectInWindow & clipRect.translated(p: rectInWindow.topLeft());
289 const QRect srcRect = toBottomLeftRect(topLeftRect: clipRect, windowHeight: rectInWindow.height());
290
291 const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(target: deviceRect(rect: clippedRectInWindow, window),
292 viewport: deviceWindowRect);
293
294 const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(subTexture: deviceRect(rect: srcRect, window),
295 textureSize: deviceRect(rect: rectInWindow, window).size(),
296 origin: QOpenGLTextureBlitter::OriginBottomLeft);
297
298 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
299 const bool srgb = textures->flags(index: idx).testFlag(flag: QPlatformTextureList::TextureIsSrgb);
300 if (srgb && canUseSrgb)
301 funcs->glEnable(GL_FRAMEBUFFER_SRGB);
302
303 blitter->blit(texture: textures->textureId(index: idx), targetTransform: target, sourceTransform: 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
322void 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(other: 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(window: 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(surface: 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(surface: 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(x: 0, y: 0, width: qRound(d: window->width() * window->devicePixelRatio()), height: qRound(d: window->height() * window->devicePixelRatio()));
362 funcs->glClearColor(red: 0, green: 0, blue: 0, alpha: 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(rect: QRect(QPoint(), window->size()), window);
373 const QPoint deviceWindowOffset = deviceOffset(pt: 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(index: i).testFlag(flag: QPlatformTextureList::TextureIsSrgb)) {
380 GLint cap = 0;
381 funcs->glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE, params: &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(index: i).testFlag(flag: QPlatformTextureList::StacksOnTop))
391 blitTextureForWidget(textures, idx: i, window, deviceWindowRect, blitter: 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(n: 1, textures: &d_ptr->textureId);
401 funcs->glGenTextures(n: 1, textures: &d_ptr->textureId);
402 funcs->glBindTexture(GL_TEXTURE_2D, texture: 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, param: 0);
406 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, param: 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, swizzleRandB: &d_ptr->needsSwizzle, premultipliedB: &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, texture: d_ptr->textureId);
422 QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, swizzleRandB: &d_ptr->needsSwizzle, premultipliedB: &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(dirtyRegion: deviceRegion(region, window, offset), textureSize: &d_ptr->textureSize, flags: &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(topLeftRect: deviceWindowRect.translated(p: deviceWindowOffset), windowHeight: d_ptr->textureSize.height());
450 const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(subTexture: srcRect,
451 textureSize: d_ptr->textureSize,
452 origin);
453 d_ptr->blitter->blit(texture: textureId, targetTransform: QMatrix4x4(), sourceTransform: 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(index: i);
462 if (flags.testFlag(flag: 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(flag: QPlatformTextureList::StacksOnTop))
474 blitTextureForWidget(textures, idx: i, window, deviceWindowRect, blitter: d_ptr->blitter, offset, canUseSrgb);
475 }
476
477 funcs->glDisable(GL_BLEND);
478 d_ptr->blitter->release();
479
480 d_ptr->context->swapBuffers(surface: 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 */
491QImage 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 */
524GLuint 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(f: 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, param: 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(n: 1, textures: &d_ptr->textureId);
612 funcs->glGenTextures(n: 1, textures: &d_ptr->textureId);
613 funcs->glBindTexture(GL_TEXTURE_2D, texture: d_ptr->textureId);
614 if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
615 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, param: 0);
616 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, param: 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, level: 0, internalformat: internalFormat, width: imageSize.width(), height: imageSize.height(), border: 0, GL_RGBA, type: pixelType,
624 pixels: const_cast<uchar*>(image.constBits()));
625 } else {
626 funcs->glBindTexture(GL_TEXTURE_2D, texture: d_ptr->textureId);
627 QRect imageRect = image.rect();
628 QRect rect = dirtyRegion.boundingRect() & imageRect;
629
630 if (hasUnpackRowLength) {
631 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: rect.x(), yoffset: rect.y(), width: rect.width(), height: rect.height(), GL_RGBA, type: pixelType,
632 pixels: 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, level: 0, xoffset: 0, yoffset: rect.y(), width: rect.width(), height: rect.height(), GL_RGBA, type: pixelType,
646 pixels: image.constScanLine(rect.y()));
647 } else {
648 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: rect.x(), yoffset: rect.y(), width: rect.width(), height: rect.height(), GL_RGBA, type: pixelType,
649 pixels: image.copy(rect).constBits());
650 }
651 }
652 }
653
654 if (hasUnpackRowLength)
655 funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, param: 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*/
670QPlatformBackingStore::QPlatformBackingStore(QWindow *window)
671 : d_ptr(new QPlatformBackingStorePrivate(window))
672{
673}
674
675/*!
676 Destroys this surface.
677*/
678QPlatformBackingStore::~QPlatformBackingStore()
679{
680 delete d_ptr;
681}
682
683/*!
684 Returns a pointer to the top-level window associated with this
685 surface.
686*/
687QWindow* QPlatformBackingStore::window() const
688{
689 return d_ptr->window;
690}
691
692/*!
693 Sets the backing store associated with this surface.
694*/
695void 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*/
704QBackingStore *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
716void 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
726void QPlatformBackingStore::endPaint()
727{
728}
729
730/*!
731 Accessor for a backingstores graphics buffer abstraction
732*/
733QPlatformGraphicsBuffer *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*/
744bool 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
753QT_END_NAMESPACE
754

source code of qtbase/src/gui/painting/qplatformbackingstore.cpp