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 <qbackingstore.h>
41#include <qwindow.h>
42#include <qpixmap.h>
43#include <qpa/qplatformbackingstore.h>
44#include <qpa/qplatformintegration.h>
45#include <qscreen.h>
46#include <qdebug.h>
47#include <qscopedpointer.h>
48
49#include <private/qguiapplication_p.h>
50#include <private/qwindow_p.h>
51
52#include <private/qhighdpiscaling_p.h>
53
54QT_BEGIN_NAMESPACE
55
56class QBackingStorePrivate
57{
58public:
59 QBackingStorePrivate(QWindow *w)
60 : window(w)
61 {
62 }
63
64 QWindow *window;
65 QPlatformBackingStore *platformBackingStore = nullptr;
66 QScopedPointer<QImage> highDpiBackingstore;
67 QRegion staticContents;
68 QSize size;
69};
70
71/*!
72 \class QBackingStore
73 \since 5.0
74 \inmodule QtGui
75
76 \brief The QBackingStore class provides a drawing area for QWindow.
77
78 QBackingStore enables the use of QPainter to paint on a QWindow with type
79 RasterSurface. The other way of rendering to a QWindow is through the use
80 of OpenGL with QOpenGLContext.
81
82 A QBackingStore contains a buffered representation of the window contents,
83 and thus supports partial updates by using QPainter to only update a sub
84 region of the window contents.
85
86 QBackingStore might be used by an application that wants to use QPainter
87 without OpenGL acceleration and without the extra overhead of using the
88 QWidget or QGraphicsView UI stacks. For an example of how to use
89 QBackingStore see the \l{Raster Window Example}.
90*/
91
92/*!
93 Constructs an empty surface for the given top-level \a window.
94*/
95QBackingStore::QBackingStore(QWindow *window)
96 : d_ptr(new QBackingStorePrivate(window))
97{
98 if (window->handle()) {
99 // Create platform backingstore up front if we have a platform window,
100 // otherwise delay the creation until absolutely necessary.
101 handle();
102 }
103}
104
105/*!
106 Destroys this surface.
107*/
108QBackingStore::~QBackingStore()
109{
110 delete d_ptr->platformBackingStore;
111}
112
113/*!
114 Returns a pointer to the top-level window associated with this
115 surface.
116*/
117QWindow* QBackingStore::window() const
118{
119 return d_ptr->window;
120}
121
122/*!
123 Begins painting on the backing store surface in the given \a region.
124
125 You should call this function before using the paintDevice() to
126 paint.
127
128 \sa endPaint(), paintDevice()
129*/
130
131void QBackingStore::beginPaint(const QRegion &region)
132{
133 if (d_ptr->highDpiBackingstore &&
134 d_ptr->highDpiBackingstore->devicePixelRatio() != d_ptr->window->devicePixelRatio())
135 resize(size());
136
137 QPlatformBackingStore *platformBackingStore = handle();
138 platformBackingStore->beginPaint(QHighDpi::toNativeLocalRegion(region, d_ptr->window));
139
140 // When QtGui is applying a high-dpi scale factor the backing store
141 // creates a "large" backing store image. This image needs to be
142 // painted on as a high-dpi image, which is done by setting
143 // devicePixelRatio. Do this on a separate image instance that shares
144 // the image data to avoid having the new devicePixelRatio be propagated
145 // back to the platform plugin.
146 QPaintDevice *device = platformBackingStore->paintDevice();
147 if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image) {
148 QImage *source = static_cast<QImage *>(device);
149 const bool needsNewImage = d_ptr->highDpiBackingstore.isNull()
150 || source->data_ptr() != d_ptr->highDpiBackingstore->data_ptr()
151 || source->size() != d_ptr->highDpiBackingstore->size()
152 || source->devicePixelRatio() != d_ptr->highDpiBackingstore->devicePixelRatio();
153 if (needsNewImage) {
154 qCDebug(lcScaling) << "QBackingStore::beginPaint new backingstore for" << d_ptr->window;
155 qCDebug(lcScaling) << " source size" << source->size() << "dpr" << source->devicePixelRatio();
156 d_ptr->highDpiBackingstore.reset(
157 new QImage(source->bits(), source->width(), source->height(), source->bytesPerLine(), source->format()));
158
159 qreal targetDevicePixelRatio = d_ptr->window->devicePixelRatio();
160 d_ptr->highDpiBackingstore->setDevicePixelRatio(targetDevicePixelRatio);
161 qCDebug(lcScaling) <<" destination size" << d_ptr->highDpiBackingstore->size()
162 << "dpr" << targetDevicePixelRatio;
163 }
164 }
165}
166
167/*!
168 Returns the paint device for this surface.
169
170 \warning The device is only valid between calls to beginPaint() and
171 endPaint(). You should not cache the returned value.
172*/
173QPaintDevice *QBackingStore::paintDevice()
174{
175 QPaintDevice *device = handle()->paintDevice();
176
177 if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image)
178 return d_ptr->highDpiBackingstore.data();
179
180 return device;
181}
182
183/*!
184 Ends painting.
185
186 You should call this function after painting with the paintDevice()
187 has ended.
188
189 \sa beginPaint(), paintDevice()
190*/
191void QBackingStore::endPaint()
192{
193 if (paintDevice()->paintingActive())
194 qWarning("QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?");
195
196 handle()->endPaint();
197}
198
199static bool isRasterSurface(QWindow *window)
200{
201 switch (window->surfaceType()) {
202 case QSurface::RasterSurface:
203 case QSurface::RasterGLSurface:
204 return true;
205 default:
206 return false;
207 };
208}
209
210/*!
211 Flushes the given \a region from the specified \a window onto the
212 screen.
213
214 The \a window must either be the top level window represented by
215 this backingstore, or a non-transient child of that window. Passing
216 \nullptr falls back to using the backingstore's top level window.
217
218 If the \a window is a child window, the \a region should be in child window
219 coordinates, and the \a offset should be the child window's offset in relation
220 to the backingstore's top level window.
221
222 You should call this function after ending painting with endPaint().
223*/
224void QBackingStore::flush(const QRegion &region, QWindow *window, const QPoint &offset)
225{
226 QWindow *topLevelWindow = this->window();
227
228 if (!window)
229 window = topLevelWindow;
230 if (!window->handle()) {
231 qWarning() << "QBackingStore::flush() called for "
232 << window << " which does not have a handle.";
233 return;
234 }
235
236 if (!isRasterSurface(window)) {
237 qWarning() << "Attempted flush to non-raster surface" << window << "of type" << window->surfaceType()
238 << (window->inherits("QWidgetWindow") ? "(consider using Qt::WA_PaintOnScreen to exclude "
239 "from backingstore sync)" : "");
240 return;
241 }
242
243 Q_ASSERT(window == topLevelWindow || topLevelWindow->isAncestorOf(window, QWindow::ExcludeTransients));
244
245 handle()->flush(window, QHighDpi::toNativeLocalRegion(region, window),
246 QHighDpi::toNativeLocalPosition(offset, window));
247}
248
249/*!
250 Sets the size of the window surface to \a size.
251
252 \sa size()
253*/
254void QBackingStore::resize(const QSize &size)
255{
256 d_ptr->size = size;
257 handle()->resize(QHighDpi::toNativePixels(size, d_ptr->window), d_ptr->staticContents);
258}
259
260/*!
261 Returns the current size of the window surface.
262*/
263QSize QBackingStore::size() const
264{
265 return d_ptr->size;
266}
267
268/*!
269 Scrolls the given \a area \a dx pixels to the right and \a dy
270 downward; both \a dx and \a dy may be negative.
271
272 Returns \c true if the area was scrolled successfully; false otherwise.
273*/
274bool QBackingStore::scroll(const QRegion &area, int dx, int dy)
275{
276 // Disable scrolling for non-integer scroll deltas. For this case
277 // the existing rendered pixels can't be re-used, and we return
278 // false to signal that a repaint is needed.
279 const qreal nativeDx = QHighDpi::toNativePixels(qreal(dx), d_ptr->window);
280 const qreal nativeDy = QHighDpi::toNativePixels(qreal(dy), d_ptr->window);
281 if (qFloor(nativeDx) != nativeDx || qFloor(nativeDy) != nativeDy)
282 return false;
283
284 return handle()->scroll(QHighDpi::toNativeLocalRegion(area, d_ptr->window),
285 nativeDx, nativeDy);
286}
287
288/*!
289 Set \a region as the static contents of this window.
290*/
291void QBackingStore::setStaticContents(const QRegion &region)
292{
293 d_ptr->staticContents = region;
294}
295
296/*!
297 Returns a QRegion representing the area of the window that
298 has static contents.
299*/
300QRegion QBackingStore::staticContents() const
301{
302 return d_ptr->staticContents;
303}
304
305/*!
306 Returns a boolean indicating if this window has static contents or not.
307*/
308bool QBackingStore::hasStaticContents() const
309{
310 return !d_ptr->staticContents.isEmpty();
311}
312
313void Q_GUI_EXPORT qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset)
314{
315 // make sure we don't detach
316 uchar *mem = const_cast<uchar*>(const_cast<const QImage &>(img).bits());
317
318 qsizetype lineskip = img.bytesPerLine();
319 int depth = img.depth() >> 3;
320
321 const QRect imageRect(0, 0, img.width(), img.height());
322 const QRect r = rect & imageRect & imageRect.translated(-offset);
323 const QPoint p = rect.topLeft() + offset;
324
325 if (r.isEmpty())
326 return;
327
328 const uchar *src;
329 uchar *dest;
330
331 if (r.top() < p.y()) {
332 src = mem + r.bottom() * lineskip + r.left() * depth;
333 dest = mem + (p.y() + r.height() - 1) * lineskip + p.x() * depth;
334 lineskip = -lineskip;
335 } else {
336 src = mem + r.top() * lineskip + r.left() * depth;
337 dest = mem + p.y() * lineskip + p.x() * depth;
338 }
339
340 const int w = r.width();
341 int h = r.height();
342 const int bytes = w * depth;
343
344 // overlapping segments?
345 if (offset.y() == 0 && qAbs(offset.x()) < w) {
346 do {
347 ::memmove(dest, src, bytes);
348 dest += lineskip;
349 src += lineskip;
350 } while (--h);
351 } else {
352 do {
353 ::memcpy(dest, src, bytes);
354 dest += lineskip;
355 src += lineskip;
356 } while (--h);
357 }
358}
359
360/*!
361 Returns a pointer to the QPlatformBackingStore implementation
362*/
363QPlatformBackingStore *QBackingStore::handle() const
364{
365 if (!d_ptr->platformBackingStore) {
366 d_ptr->platformBackingStore = QGuiApplicationPrivate::platformIntegration()->createPlatformBackingStore(d_ptr->window);
367 d_ptr->platformBackingStore->setBackingStore(const_cast<QBackingStore*>(this));
368 }
369 return d_ptr->platformBackingStore;
370}
371
372QT_END_NAMESPACE
373