1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
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 General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "qwasmbackingstore.h"
31 #include "qwasmwindow.h"
32 #include "qwasmcompositor.h"
33 
34 #include <QtGui/qopengltexture.h>
35 #include <QtGui/qmatrix4x4.h>
36 #include <QtGui/qpainter.h>
37 #include <private/qguiapplication_p.h>
38 #include <qpa/qplatformscreen.h>
39 #include <QtGui/qoffscreensurface.h>
40 #include <QtGui/qbackingstore.h>
41 
42 QT_BEGIN_NAMESPACE
43 
QWasmBackingStore(QWasmCompositor * compositor,QWindow * window)44 QWasmBackingStore::QWasmBackingStore(QWasmCompositor *compositor, QWindow *window)
45     : QPlatformBackingStore(window)
46     , m_compositor(compositor)
47     , m_texture(new QOpenGLTexture(QOpenGLTexture::Target2D))
48 {
49     QWasmWindow *wasmWindow = static_cast<QWasmWindow *>(window->handle());
50     if (wasmWindow)
51         wasmWindow->setBackingStore(this);
52 }
53 
~QWasmBackingStore()54 QWasmBackingStore::~QWasmBackingStore()
55 {
56     auto window = this->window();
57     QWasmIntegration::get()->removeBackingStore(window);
58     destroy();
59     QWasmWindow *wasmWindow = static_cast<QWasmWindow *>(window->handle());
60     if (wasmWindow)
61         wasmWindow->setBackingStore(nullptr);
62 }
63 
destroy()64 void QWasmBackingStore::destroy()
65 {
66     if (m_texture->isCreated()) {
67         auto context = m_compositor->context();
68         auto currentContext = QOpenGLContext::currentContext();
69         if (!currentContext || !QOpenGLContext::areSharing(context, currentContext)) {
70             QOffscreenSurface offScreenSurface(m_compositor->screen()->screen());
71             offScreenSurface.setFormat(context->format());
72             offScreenSurface.create();
73             context->makeCurrent(&offScreenSurface);
74             m_texture->destroy();
75         } else {
76             m_texture->destroy();
77         }
78     }
79 }
80 
paintDevice()81 QPaintDevice *QWasmBackingStore::paintDevice()
82 {
83     return &m_image;
84 }
85 
flush(QWindow * window,const QRegion & region,const QPoint & offset)86 void QWasmBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
87 {
88     Q_UNUSED(window);
89     Q_UNUSED(region);
90     Q_UNUSED(offset);
91 
92     m_dirty |= region;
93     m_compositor->requestRedraw();
94 }
95 
updateTexture()96 void QWasmBackingStore::updateTexture()
97 {
98     if (m_dirty.isNull())
99         return;
100 
101     if (m_recreateTexture) {
102         m_recreateTexture = false;
103         destroy();
104     }
105 
106     if (!m_texture->isCreated()) {
107         m_texture->setMinificationFilter(QOpenGLTexture::Nearest);
108         m_texture->setMagnificationFilter(QOpenGLTexture::Nearest);
109         m_texture->setWrapMode(QOpenGLTexture::ClampToEdge);
110         m_texture->setData(m_image, QOpenGLTexture::DontGenerateMipMaps);
111         m_texture->create();
112     }
113     m_texture->bind();
114 
115     QRegion fixed;
116     QRect imageRect = m_image.rect();
117 
118     for (const QRect &rect : m_dirty) {
119 
120         // Convert device-independent dirty region to device region
121         qreal dpr = m_image.devicePixelRatio();
122         QRect deviceRect = QRect(rect.topLeft() * dpr, rect.size() * dpr);
123 
124         // intersect with image rect to be sure
125         QRect r = imageRect & deviceRect;
126         // if the rect is wide enough it is cheaper to just extend it instead of doing an image copy
127         if (r.width() >= imageRect.width() / 2) {
128             r.setX(0);
129             r.setWidth(imageRect.width());
130         }
131 
132         fixed |= r;
133     }
134 
135     for (const QRect &rect : fixed) {
136         // if the sub-rect is full-width we can pass the image data directly to
137         // OpenGL instead of copying, since there is no gap between scanlines
138         if (rect.width() == imageRect.width()) {
139             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
140                             m_image.constScanLine(rect.y()));
141         } else {
142             glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
143                             m_image.copy(rect).constBits());
144         }
145     }
146     /* End of code taken from QEGLPlatformBackingStore */
147 
148     m_dirty = QRegion();
149 }
150 
beginPaint(const QRegion & region)151 void QWasmBackingStore::beginPaint(const QRegion &region)
152 {
153     m_dirty |= region;
154     // Keep backing store device pixel ratio in sync with window
155     if (m_image.devicePixelRatio() != window()->devicePixelRatio())
156         resize(backingStore()->size(), backingStore()->staticContents());
157 
158     QPainter painter(&m_image);
159     painter.setCompositionMode(QPainter::CompositionMode_Source);
160     const QColor blank = Qt::transparent;
161     for (const QRect &rect : region)
162         painter.fillRect(rect, blank);
163 }
164 
resize(const QSize & size,const QRegion & staticContents)165 void QWasmBackingStore::resize(const QSize &size, const QRegion &staticContents)
166 {
167     Q_UNUSED(staticContents)
168 
169     m_image = QImage(size * window()->devicePixelRatio(), QImage::Format_RGB32);
170     m_image.setDevicePixelRatio(window()->devicePixelRatio());
171     m_recreateTexture = true;
172 }
173 
toImage() const174 QImage QWasmBackingStore::toImage() const
175 {
176     // used by QPlatformBackingStore::composeAndFlush
177     return m_image;
178 }
179 
getImageRef() const180 const QImage &QWasmBackingStore::getImageRef() const
181 {
182     return m_image;
183 }
184 
getUpdatedTexture()185 const QOpenGLTexture *QWasmBackingStore::getUpdatedTexture()
186 {
187     updateTexture();
188     return m_texture.data();
189 }
190 
191 QT_END_NAMESPACE
192