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