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(0)
91#ifndef QT_NO_OPENGL
92 , textureId(0)
93 , blitter(0)
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(&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
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(index).textureId;
170}
171
172void *QPlatformTextureList::source(int index)
173{
174 Q_D(const QPlatformTextureList);
175 return d->textures.at(index).source;
176}
177
178QPlatformTextureList::Flags QPlatformTextureList::flags(int index) const
179{
180 Q_D(const QPlatformTextureList);
181 return d->textures.at(index).flags;
182}
183
184QRect QPlatformTextureList::geometry(int index) const
185{
186 Q_D(const QPlatformTextureList);
187 return d->textures.at(index).rect;
188}
189
190QRect QPlatformTextureList::clipRect(int index) const
191{
192 Q_D(const QPlatformTextureList);
193 return d->textures.at(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(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(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
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(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
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(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 if (!d_ptr->context->makeCurrent(window)) {
342 qCWarning(lcQpaBackingStore, "composeAndFlush: makeCurrent() failed");
343 return;
344 }
345
346 qCDebug(lcQpaBackingStore) << "Composing and flushing" << region << "of" << window
347 << "at offset" << offset << "with" << textures->count() << "texture(s) in" << textures;
348
349 QWindowPrivate::get(window)->lastComposeTime.start();
350
351 QOpenGLFunctions *funcs = d_ptr->context->functions();
352 funcs->glViewport(0, 0, qRound(window->width() * window->devicePixelRatio()), qRound(window->height() * window->devicePixelRatio()));
353 funcs->glClearColor(0, 0, 0, translucentBackground ? 0 : 1);
354 funcs->glClear(GL_COLOR_BUFFER_BIT);
355
356 if (!d_ptr->blitter) {
357 d_ptr->blitter = new QOpenGLTextureBlitter;
358 d_ptr->blitter->create();
359 }
360
361 d_ptr->blitter->bind();
362
363 const QRect deviceWindowRect = deviceRect(QRect(QPoint(), window->size()), window);
364 const QPoint deviceWindowOffset = deviceOffset(offset, window);
365
366 bool canUseSrgb = false;
367 // If there are any sRGB textures in the list, check if the destination
368 // framebuffer is sRGB capable.
369 for (int i = 0; i < textures->count(); ++i) {
370 if (textures->flags(i).testFlag(QPlatformTextureList::TextureIsSrgb)) {
371 GLint cap = 0;
372 funcs->glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE, &cap);
373 if (cap)
374 canUseSrgb = true;
375 break;
376 }
377 }
378
379 // Textures for renderToTexture widgets.
380 for (int i = 0; i < textures->count(); ++i) {
381 if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop))
382 blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset, canUseSrgb);
383 }
384
385 // Backingstore texture with the normal widgets.
386 GLuint textureId = 0;
387 QOpenGLTextureBlitter::Origin origin = QOpenGLTextureBlitter::OriginTopLeft;
388 if (QPlatformGraphicsBuffer *graphicsBuffer = this->graphicsBuffer()) {
389 if (graphicsBuffer->size() != d_ptr->textureSize) {
390 if (d_ptr->textureId)
391 funcs->glDeleteTextures(1, &d_ptr->textureId);
392 funcs->glGenTextures(1, &d_ptr->textureId);
393 funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId);
394 QOpenGLContext *ctx = QOpenGLContext::currentContext();
395 if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
396 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
397 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
398 }
399 funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
400 funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
401 funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
402 funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
403
404 if (QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &d_ptr->needsSwizzle, &d_ptr->premultiplied)) {
405 d_ptr->textureSize = graphicsBuffer->size();
406 } else {
407 d_ptr->textureSize = QSize(0,0);
408 }
409
410 graphicsBuffer->unlock();
411 } else if (!region.isEmpty()){
412 funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId);
413 QPlatformGraphicsBufferHelper::lockAndBindToTexture(graphicsBuffer, &d_ptr->needsSwizzle, &d_ptr->premultiplied);
414 graphicsBuffer->unlock();
415 }
416
417 if (graphicsBuffer->origin() == QPlatformGraphicsBuffer::OriginBottomLeft)
418 origin = QOpenGLTextureBlitter::OriginBottomLeft;
419 textureId = d_ptr->textureId;
420 } else {
421 TextureFlags flags = 0;
422 textureId = toTexture(deviceRegion(region, window, offset), &d_ptr->textureSize, &flags);
423 d_ptr->needsSwizzle = (flags & TextureSwizzle) != 0;
424 d_ptr->premultiplied = (flags & TexturePremultiplied) != 0;
425 if (flags & TextureFlip)
426 origin = QOpenGLTextureBlitter::OriginBottomLeft;
427 }
428
429 funcs->glEnable(GL_BLEND);
430 if (d_ptr->premultiplied)
431 funcs->glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
432 else
433 funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
434
435 if (textureId) {
436 if (d_ptr->needsSwizzle)
437 d_ptr->blitter->setRedBlueSwizzle(true);
438 // The backingstore is for the entire tlw.
439 // In case of native children offset tells the position relative to the tlw.
440 const QRect srcRect = toBottomLeftRect(deviceWindowRect.translated(deviceWindowOffset), d_ptr->textureSize.height());
441 const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect,
442 d_ptr->textureSize,
443 origin);
444 d_ptr->blitter->blit(textureId, QMatrix4x4(), source);
445 if (d_ptr->needsSwizzle)
446 d_ptr->blitter->setRedBlueSwizzle(false);
447 }
448
449 // There is no way to tell if the OpenGL-rendered content is premultiplied or not.
450 // For compatibility, assume that it is not, and use normal alpha blend always.
451 if (d_ptr->premultiplied)
452 funcs->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
453
454 // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set.
455 for (int i = 0; i < textures->count(); ++i) {
456 if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop))
457 blitTextureForWidget(textures, i, window, deviceWindowRect, d_ptr->blitter, offset, canUseSrgb);
458 }
459
460 funcs->glDisable(GL_BLEND);
461 d_ptr->blitter->release();
462
463 d_ptr->context->swapBuffers(window);
464}
465#endif
466/*!
467 Implemented in subclasses to return the content of the backingstore as a QImage.
468
469 If QPlatformIntegration::RasterGLSurface is supported, either this function or
470 toTexture() must be implemented.
471
472 \sa toTexture()
473 */
474QImage QPlatformBackingStore::toImage() const
475{
476 return QImage();
477}
478#ifndef QT_NO_OPENGL
479/*!
480 May be reimplemented in subclasses to return the content of the
481 backingstore as an OpenGL texture. \a dirtyRegion is the part of the
482 backingstore which may have changed since the last call to this function. The
483 caller of this function must ensure that there is a current context.
484
485 The size of the texture is returned in \a textureSize.
486
487 The ownership of the texture is not transferred. The caller must not store
488 the return value between calls, but instead call this function before each use.
489
490 The default implementation returns a cached texture if \a dirtyRegion is empty and
491 \a textureSize matches the backingstore size, otherwise it retrieves the content using
492 toImage() and performs a texture upload. This works only if the value of \a textureSize
493 is preserved between the calls to this function.
494
495 If the red and blue components have to swapped, \a flags will be set to include \c
496 TextureSwizzle. This allows creating textures from images in formats like
497 QImage::Format_RGB32 without any further image conversion. Instead, the swizzling will
498 be done in the shaders when performing composition. Other formats, that do not need
499 such swizzling due to being already byte ordered RGBA, for example
500 QImage::Format_RGBA8888, must result in having \a needsSwizzle set to false.
501
502 If the image has to be flipped (e.g. because the texture is attached to an FBO), \a
503 flags will be set to include \c TextureFlip.
504
505 \note \a dirtyRegion is relative to the backingstore so no adjustment is needed.
506 */
507GLuint QPlatformBackingStore::toTexture(const QRegion &dirtyRegion, QSize *textureSize, TextureFlags *flags) const
508{
509 Q_ASSERT(textureSize);
510 Q_ASSERT(flags);
511
512 QImage image = toImage();
513 QSize imageSize = image.size();
514
515 QOpenGLContext *ctx = QOpenGLContext::currentContext();
516 GLenum internalFormat = GL_RGBA;
517 GLuint pixelType = GL_UNSIGNED_BYTE;
518
519 bool needsConversion = false;
520 *flags = 0;
521 switch (image.format()) {
522 case QImage::Format_ARGB32_Premultiplied:
523 *flags |= TexturePremultiplied;
524 Q_FALLTHROUGH();
525 case QImage::Format_RGB32:
526 case QImage::Format_ARGB32:
527 *flags |= TextureSwizzle;
528 break;
529 case QImage::Format_RGBA8888_Premultiplied:
530 *flags |= TexturePremultiplied;
531 Q_FALLTHROUGH();
532 case QImage::Format_RGBX8888:
533 case QImage::Format_RGBA8888:
534 break;
535 case QImage::Format_BGR30:
536 case QImage::Format_A2BGR30_Premultiplied:
537 if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
538 pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
539 internalFormat = GL_RGB10_A2;
540 *flags |= TexturePremultiplied;
541 } else {
542 needsConversion = true;
543 }
544 break;
545 case QImage::Format_RGB30:
546 case QImage::Format_A2RGB30_Premultiplied:
547 if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
548 pixelType = GL_UNSIGNED_INT_2_10_10_10_REV;
549 internalFormat = GL_RGB10_A2;
550 *flags |= TextureSwizzle | TexturePremultiplied;
551 } else {
552 needsConversion = true;
553 }
554 break;
555 default:
556 needsConversion = true;
557 break;
558 }
559 if (imageSize.isEmpty()) {
560 *textureSize = imageSize;
561 return 0;
562 }
563
564 // Must rely on the input only, not d_ptr.
565 // With the default composeAndFlush() textureSize is &d_ptr->textureSize.
566 bool resized = *textureSize != imageSize;
567 if (dirtyRegion.isEmpty() && !resized)
568 return d_ptr->textureId;
569
570 *textureSize = imageSize;
571
572 if (needsConversion)
573 image = image.convertToFormat(QImage::Format_RGBA8888);
574
575 // The image provided by the backingstore may have a stride larger than width * 4, for
576 // instance on platforms that manually implement client-side decorations.
577 static const int bytesPerPixel = 4;
578 const int strideInPixels = image.bytesPerLine() / bytesPerPixel;
579 const bool hasUnpackRowLength = !ctx->isOpenGLES() || ctx->format().majorVersion() >= 3;
580
581 QOpenGLFunctions *funcs = ctx->functions();
582
583 if (hasUnpackRowLength) {
584 funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, strideInPixels);
585 } else if (strideInPixels != image.width()) {
586 // No UNPACK_ROW_LENGTH on ES 2.0 and yet we would need it. This case is typically
587 // hit with QtWayland which is rarely used in combination with a ES2.0-only GL
588 // implementation. Therefore, accept the performance hit and do a copy.
589 image = image.copy();
590 }
591
592 if (resized) {
593 if (d_ptr->textureId)
594 funcs->glDeleteTextures(1, &d_ptr->textureId);
595 funcs->glGenTextures(1, &d_ptr->textureId);
596 funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId);
597 if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
598 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
599 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
600 }
601 funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
602 funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
603 funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
604 funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
605
606 funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, imageSize.width(), imageSize.height(), 0, GL_RGBA, pixelType,
607 const_cast<uchar*>(image.constBits()));
608 } else {
609 funcs->glBindTexture(GL_TEXTURE_2D, d_ptr->textureId);
610 QRect imageRect = image.rect();
611 QRect rect = dirtyRegion.boundingRect() & imageRect;
612
613 if (hasUnpackRowLength) {
614 funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
615 image.constScanLine(rect.y()) + rect.x() * bytesPerPixel);
616 } else {
617 // if the rect is wide enough it's cheaper to just
618 // extend it instead of doing an image copy
619 if (rect.width() >= imageRect.width() / 2) {
620 rect.setX(0);
621 rect.setWidth(imageRect.width());
622 }
623
624 // if the sub-rect is full-width we can pass the image data directly to
625 // OpenGL instead of copying, since there's no gap between scanlines
626
627 if (rect.width() == imageRect.width()) {
628 funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
629 image.constScanLine(rect.y()));
630 } else {
631 funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, pixelType,
632 image.copy(rect).constBits());
633 }
634 }
635 }
636
637 if (hasUnpackRowLength)
638 funcs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
639
640 return d_ptr->textureId;
641}
642#endif // QT_NO_OPENGL
643
644/*!
645 \fn QPaintDevice* QPlatformBackingStore::paintDevice()
646
647 Implement this function to return the appropriate paint device.
648*/
649
650/*!
651 Constructs an empty surface for the given top-level \a window.
652*/
653QPlatformBackingStore::QPlatformBackingStore(QWindow *window)
654 : d_ptr(new QPlatformBackingStorePrivate(window))
655{
656}
657
658/*!
659 Destroys this surface.
660*/
661QPlatformBackingStore::~QPlatformBackingStore()
662{
663 delete d_ptr;
664}
665
666/*!
667 Returns a pointer to the top-level window associated with this
668 surface.
669*/
670QWindow* QPlatformBackingStore::window() const
671{
672 return d_ptr->window;
673}
674
675/*!
676 Sets the backing store associated with this surface.
677*/
678void QPlatformBackingStore::setBackingStore(QBackingStore *backingStore)
679{
680 d_ptr->backingStore = backingStore;
681}
682
683/*!
684 Returns a pointer to the backing store associated with this
685 surface.
686*/
687QBackingStore *QPlatformBackingStore::backingStore() const
688{
689 return d_ptr->backingStore;
690}
691
692/*!
693 This function is called before painting onto the surface begins,
694 with the \a region in which the painting will occur.
695
696 \sa endPaint(), paintDevice()
697*/
698
699void QPlatformBackingStore::beginPaint(const QRegion &)
700{
701}
702
703/*!
704 This function is called after painting onto the surface has ended.
705
706 \sa beginPaint(), paintDevice()
707*/
708
709void QPlatformBackingStore::endPaint()
710{
711}
712
713/*!
714 Accessor for a backingstores graphics buffer abstraction
715*/
716QPlatformGraphicsBuffer *QPlatformBackingStore::graphicsBuffer() const
717{
718 return nullptr;
719}
720
721/*!
722 Scrolls the given \a area \a dx pixels to the right and \a dy
723 downward; both \a dx and \a dy may be negative.
724
725 Returns \c true if the area was scrolled successfully; false otherwise.
726*/
727bool QPlatformBackingStore::scroll(const QRegion &area, int dx, int dy)
728{
729 Q_UNUSED(area);
730 Q_UNUSED(dx);
731 Q_UNUSED(dy);
732
733 return false;
734}
735
736QT_END_NAMESPACE
737