Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcocoabackingstore.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <AppKit/AppKit.h>
5
7
8#include "qcocoawindow.h"
9#include "qcocoahelpers.h"
10
11#include <QtCore/qmath.h>
12#include <QtCore/private/qcore_mac_p.h>
13#include <QtGui/qpainter.h>
14
15#include <QuartzCore/CATransaction.h>
16
18
23
24QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const
25{
26 const auto *platformWindow = static_cast<QCocoaWindow *>(window()->handle());
27 const QNSView *view = qnsview_cast(platformWindow->view());
28 return QCFType<CGColorSpaceRef>::constructFromGet(view.colorSpace.CGColorSpace);
29}
30
31// ----------------------------------------------------------------------------
32
35{
36 qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window
37 << "with" << window->format();
38
39 m_buffers.resize(1);
40
41 observeBackingPropertiesChanges();
43}
44
48
49void QCALayerBackingStore::observeBackingPropertiesChanges()
50{
52 NSView *view = static_cast<QCocoaWindow *>(window()->handle())->view();
53 m_backingPropertiesObserver = QMacNotificationObserver(view.window,
54 NSWindowDidChangeBackingPropertiesNotification, [this]() {
55 backingPropertiesChanged();
56 });
57}
58
60{
61 Q_ASSERT(watched == window());
62
63 if (event->type() == QEvent::PlatformSurface) {
64 auto *surfaceEvent = static_cast<QPlatformSurfaceEvent*>(event);
65 if (surfaceEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated)
66 observeBackingPropertiesChanges();
67 else
68 m_backingPropertiesObserver = QMacNotificationObserver();
69 }
70
71 return false;
72}
73
74void QCALayerBackingStore::resize(const QSize &size, const QRegion &staticContents)
75{
76 qCDebug(lcQpaBackingStore) << "Resize requested to" << size
77 << "with static contents" << staticContents;
78
79 m_requestedSize = size;
80 m_staticContents = staticContents;
81}
82
84{
85 Q_UNUSED(region);
86
88
89 qCInfo(lcQpaBackingStore) << "Beginning paint of" << region << "into backingstore of" << m_requestedSize;
90
91 ensureBackBuffer(); // Find an unused back buffer, or reserve space for a new one
92
93 const bool bufferWasRecreated = recreateBackBufferIfNeeded();
94
95 m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
96
97 // Although undocumented, QBackingStore::beginPaint expects the painted region
98 // to be cleared before use if the window has a surface format with an alpha.
99 // Fresh IOSurfaces are already cleared, so we don't need to clear those.
100 if (m_clearSurfaceOnPaint && !bufferWasRecreated && window()->format().hasAlpha()) {
101 qCDebug(lcQpaBackingStore) << "Clearing" << region << "before use";
102 QPainter painter(m_buffers.back()->asImage());
104 for (const QRect &rect : region)
106 }
107
108 // We assume the client is going to paint the entire region
109 updateDirtyStates(region);
110}
111
112void QCALayerBackingStore::ensureBackBuffer()
113{
114 if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer)
115 return;
116
117 if (Q_UNLIKELY(lcQpaBackingStore().isDebugEnabled())) {
118 // ┌───────┬───────┬───────┬─────┬──────┐
119 // │ front ┊ spare ┊ spare ┊ ... ┊ back │
120 // └───────┴───────┴───────┴─────┴──────┘
121 for (const auto &buffer : m_buffers) {
122 qCDebug(lcQpaBackingStore).nospace() << " "
123 << (buffer == m_buffers.front() ? "front" :
124 buffer == m_buffers.back() ? " back" :
125 "spare"
126 ) << ": " << buffer.get();
127 }
128 }
129
130 // Ensure our back buffer is ready to draw into. If not, find a buffer that
131 // is not in use, or reserve space for a new buffer if none can be found.
132 for (auto &buffer : backwards(m_buffers)) {
133 if (!buffer || !buffer->isInUse()) {
134 // Buffer is okey to use, swap if necessary
135 if (buffer != m_buffers.back())
136 std::swap(buffer, m_buffers.back());
137 qCDebug(lcQpaBackingStore) << "Using back buffer" << m_buffers.back().get();
138
139 static const int kMaxSwapChainDepth = 3;
140 if (m_buffers.size() > kMaxSwapChainDepth) {
141 qCDebug(lcQpaBackingStore) << "Reducing swap chain depth to" << kMaxSwapChainDepth;
142 m_buffers.erase(std::next(m_buffers.begin(), 1), std::prev(m_buffers.end(), 2));
143 }
144
145 break;
146 } else if (buffer == m_buffers.front()) {
147 // We've exhausted the available buffers, make room for a new one
148 const int swapChainDepth = m_buffers.size() + 1;
149 qCDebug(lcQpaBackingStore) << "Available buffers exhausted, increasing swap chain depth to" << swapChainDepth;
150 m_buffers.resize(swapChainDepth);
151 break;
152 }
153 }
154
155 Q_ASSERT(!m_buffers.back() || !m_buffers.back()->isInUse());
156}
157
158// Disabled until performance issue on 5K iMac Pro has been investigated further,
159// as rounding up during resize will typically result in full screen buffer sizes
160// and low frame rate also for smaller window sizes.
161#define USE_LAZY_BUFFER_ALLOCATION_DURING_LIVE_WINDOW_RESIZE 0
162
163bool QCALayerBackingStore::recreateBackBufferIfNeeded()
164{
165 const QCocoaWindow *platformWindow = static_cast<QCocoaWindow *>(window()->handle());
166 const qreal devicePixelRatio = platformWindow->devicePixelRatio();
167 QSize requestedBufferSize = m_requestedSize * devicePixelRatio;
168
169 const NSView *backingStoreView = platformWindow->view();
170 Q_UNUSED(backingStoreView);
171
172 auto bufferSizeMismatch = [&](const QSize requested, const QSize actual) {
173#if USE_LAZY_BUFFER_ALLOCATION_DURING_LIVE_WINDOW_RESIZE
174 if (backingStoreView.inLiveResize) {
175 // Prevent over-eager buffer allocation during window resize by reusing larger buffers
176 return requested.width() > actual.width() || requested.height() > actual.height();
177 }
178#endif
179 return requested != actual;
180 };
181
182 if (!m_buffers.back() || bufferSizeMismatch(requestedBufferSize, m_buffers.back()->size())) {
183#if USE_LAZY_BUFFER_ALLOCATION_DURING_LIVE_WINDOW_RESIZE
184 if (backingStoreView.inLiveResize) {
185 // Prevent over-eager buffer allocation during window resize by rounding up
186 QSize nativeScreenSize = window()->screen()->geometry().size() * devicePixelRatio;
187 requestedBufferSize = QSize(qNextPowerOfTwo(requestedBufferSize.width()),
188 qNextPowerOfTwo(requestedBufferSize.height())).boundedTo(nativeScreenSize);
189 }
190#endif
191
192 qCInfo(lcQpaBackingStore)<< "Creating surface of" << requestedBufferSize
193 << "for" << window() << "based on requested" << m_requestedSize
194 << "dpr =" << devicePixelRatio << "and color space" << colorSpace();
195
197 auto *newBackBuffer = new GraphicsBuffer(requestedBufferSize, devicePixelRatio, pixelFormat, colorSpace());
198
199 if (!m_staticContents.isEmpty() && m_buffers.back()) {
200 // We implicitly support static backingstore content as a result of
201 // finalizing the back buffer on flush, where we copy any non-painted
202 // areas from the front buffer. But there is no guarantee that a resize
203 // will always come after a flush, where we have a pristine front buffer
204 // to copy from. It may come after a few begin/endPaints, where the back
205 // buffer then contains (part of) the latest state. We also have the case
206 // of single-buffered backingstore, where the front and back buffer is
207 // the same, which means we must do the copy from the old back buffer
208 // to the newly resized buffer now, before we replace it below.
209
210 // If the back buffer has been partially filled already, we need to
211 // copy parts of the static content from that. The rest we copy from
212 // the front buffer.
213 const QRegion backBufferRegion = m_staticContents - m_buffers.back()->dirtyRegion;
214 const QRegion frontBufferRegion = m_staticContents - backBufferRegion;
215
216 qCInfo(lcQpaBackingStore) << "Preserving static content" << backBufferRegion
217 << "from back buffer, and" << frontBufferRegion << "from front buffer";
218
219 newBackBuffer->lock(QPlatformGraphicsBuffer::SWWriteAccess);
220 blitBuffer(m_buffers.back().get(), backBufferRegion, newBackBuffer);
221 Q_ASSERT(frontBufferRegion.isEmpty() || m_buffers.front());
222 blitBuffer(m_buffers.front().get(), frontBufferRegion, newBackBuffer);
223 newBackBuffer->unlock();
224
225 // The new back buffer now is valid for the static contents region.
226 // We don't need to maintain the static contents region for resizes
227 // of any other buffers in the swap chain, as these will finalize
228 // their content on flush from the buffer we just filled, and we
229 // don't need to mark them dirty for the area we just filled, as
230 // new buffers are fully dirty when created.
231 newBackBuffer->dirtyRegion -= m_staticContents;
232 m_staticContents = {};
233 }
234
235 m_buffers.back().reset(newBackBuffer);
236 return true;
237 }
238
239 return false;
240}
241
243{
244 Q_ASSERT(m_buffers.back());
245 return m_buffers.back()->asImage();
246}
247
249{
250 qCInfo(lcQpaBackingStore) << "Paint ended. Back buffer valid region is now" << m_buffers.back()->validRegion();
251 m_buffers.back()->unlock();
252
253 // Since we can have multiple begin/endPaint rounds before a flush
254 // we defer finalizing the back buffer until its content is needed.
255}
256
257bool QCALayerBackingStore::scroll(const QRegion &region, int dx, int dy)
258{
259 if (!m_buffers.back()) {
260 qCInfo(lcQpaBackingStore) << "Scroll requested with no back buffer. Ignoring.";
261 return false;
262 }
263
264 const QPoint scrollDelta(dx, dy);
265 qCInfo(lcQpaBackingStore) << "Scrolling" << region << "by" << scrollDelta;
266
267 ensureBackBuffer();
268 recreateBackBufferIfNeeded();
269
270 const QRegion inPlaceRegion = region - m_buffers.back()->dirtyRegion;
271 const QRegion frontBufferRegion = region - inPlaceRegion;
272
274
275 m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
276
277 if (!inPlaceRegion.isEmpty()) {
278 // We have to scroll everything in one go, instead of scrolling the
279 // individual rects of the region, as otherwise we may end up reading
280 // already overwritten (scrolled) pixels.
281 const QRect inPlaceBoundingRect = inPlaceRegion.boundingRect();
282
283 qCDebug(lcQpaBackingStore) << "Scrolling" << inPlaceBoundingRect << "in place";
284 QImage *backBufferImage = m_buffers.back()->asImage();
285 const qreal devicePixelRatio = backBufferImage->devicePixelRatio();
286 const QPoint devicePixelDelta = scrollDelta * devicePixelRatio;
287
288 extern void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &);
289
290 qt_scrollRectInImage(*backBufferImage,
291 QRect(inPlaceBoundingRect.topLeft() * devicePixelRatio,
292 inPlaceBoundingRect.size() * devicePixelRatio),
293 devicePixelDelta);
294 }
295
296 if (!frontBufferRegion.isEmpty()) {
297 qCDebug(lcQpaBackingStore) << "Scrolling" << frontBufferRegion << "by copying from front buffer";
298 blitBuffer(m_buffers.front().get(), frontBufferRegion, m_buffers.back().get(), scrollDelta);
299 }
300
301 m_buffers.back()->unlock();
302
303 // Mark the target region as filled. Note: We do not mark the source region
304 // as dirty, even though the content has conceptually been "moved", as that
305 // would complicate things when preserving from the front buffer. This matches
306 // the behavior of other backingstore implementations using qt_scrollRectInImage.
307 updateDirtyStates(region.translated(scrollDelta));
308
309 qCInfo(lcQpaBackingStore) << "Scroll ended. Back buffer valid region is now" << m_buffers.back()->validRegion();
310
311 return true;
312}
313
314void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region, const QPoint &offset)
315{
316 Q_UNUSED(region);
318
319 if (!m_buffers.back()) {
320 qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first";
321 return;
322 }
323
324 finalizeBackBuffer();
325
326 if (flushedWindow != window()) {
327 flushSubWindow(flushedWindow);
328 return;
329 }
330
331 if (m_buffers.front()->isInUse() && !m_buffers.front()->isDirty()) {
332 qCInfo(lcQpaBackingStore) << "Asked to flush, but front buffer is up to date. Ignoring.";
333 return;
334 }
335
337
338 NSView *flushedView = static_cast<QCocoaWindow *>(flushedWindow->handle())->view();
339
340 // If the backingstore is just flushed, without being painted to first, then we may
341 // end in a situation where the backingstore is flushed to a layer with a different
342 // scale factor than the one it was created for in beginPaint. This is the client's
343 // fault in not picking up the change in scale factor of the window and re-painting
344 // the backingstore accordingly. To smoothing things out, we warn about this situation,
345 // and change the layer's contentsScale to match the scale of the back buffer, so that
346 // we at least cover the whole layer. This is necessary since we set the view's
347 // contents placement policy to NSViewLayerContentsPlacementTopLeft, which means
348 // AppKit will not do any scaling on our behalf.
349 if (m_buffers.back()->devicePixelRatio() != flushedView.layer.contentsScale) {
350 qCWarning(lcQpaBackingStore) << "Back buffer dpr of" << m_buffers.back()->devicePixelRatio()
351 << "doesn't match" << flushedView.layer << "contents scale of" << flushedView.layer.contentsScale
352 << "- updating layer to match.";
353 flushedView.layer.contentsScale = m_buffers.back()->devicePixelRatio();
354 }
355
356 const bool isSingleBuffered = window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer;
357
358 id backBufferSurface = (__bridge id)m_buffers.back()->surface();
359
360 // Trigger a new display cycle if there isn't one. This ensures that our layer updates
361 // are committed as part of a display-cycle instead of on the next runloop pass. This
362 // means CA won't try to throttle us if we flush too fast, and we'll coalesce our flush
363 // with other pending view and layer updates.
364 flushedView.window.viewsNeedDisplay = YES;
365
366 if (isSingleBuffered) {
367 // The private API [CALayer reloadValueForKeyPath:@"contents"] would be preferable,
368 // but barring any side effects or performance issues we opt for the hammer for now.
369 flushedView.layer.contents = nil;
370 }
371
372 qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface
373 << "to" << flushedView.layer << "of" << flushedView;
374
375 flushedView.layer.contents = backBufferSurface;
376
377 if (!isSingleBuffered) {
378 // Mark the surface as in use, so that we don't end up rendering
379 // to it while it's assigned to a layer.
380 IOSurfaceIncrementUseCount(m_buffers.back()->surface());
381
382 if (m_buffers.back() != m_buffers.front()) {
383 qCInfo(lcQpaBackingStore) << "Swapping back buffer to front";
384 std::swap(m_buffers.back(), m_buffers.front());
385 IOSurfaceDecrementUseCount(m_buffers.back()->surface());
386 }
387 }
388}
389
390void QCALayerBackingStore::flushSubWindow(QWindow *subWindow)
391{
392 qCInfo(lcQpaBackingStore) << "Flushing sub-window" << subWindow
393 << "via its own backingstore";
394
395 auto &subWindowBackingStore = m_subWindowBackingstores[subWindow];
396 if (!subWindowBackingStore) {
397 subWindowBackingStore.reset(new QCALayerBackingStore(subWindow));
398 QObject::connect(subWindow, &QObject::destroyed, this, &QCALayerBackingStore::windowDestroyed);
399 subWindowBackingStore->m_clearSurfaceOnPaint = false;
400 }
401
402 auto subWindowSize = subWindow->size();
403 static const auto kNoStaticContents = QRegion();
404 subWindowBackingStore->resize(subWindowSize, kNoStaticContents);
405
406 auto subWindowLocalRect = QRect(QPoint(), subWindowSize);
407 subWindowBackingStore->beginPaint(subWindowLocalRect);
408
409 QPainter painter(subWindowBackingStore->m_buffers.back()->asImage());
411
412 NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view();
413 NSView *flushedView = static_cast<QCocoaWindow *>(subWindow->handle())->view();
414 auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView];
415 auto scale = flushedView.layer.contentsScale;
416 subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale));
417
418 m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess);
419 const QImage *backingStoreImage = m_buffers.back()->asImage();
420 painter.drawImage(subWindowLocalRect, *backingStoreImage, QRectF::fromCGRect(subviewRect));
421 m_buffers.back()->unlock();
422
423 painter.end();
424 subWindowBackingStore->endPaint();
425 subWindowBackingStore->flush(subWindow, subWindowLocalRect, QPoint());
426
427 qCInfo(lcQpaBackingStore) << "Done flushing sub-window" << subWindow;
428}
429
430void QCALayerBackingStore::windowDestroyed(QObject *object)
431{
432 auto *window = static_cast<QWindow*>(object);
433 qCInfo(lcQpaBackingStore) << "Removing backingstore for sub-window" << window;
434 m_subWindowBackingstores.erase(window);
435}
436
438 qreal sourceDevicePixelRatio,
439 const QRegion &region,
440 const QPoint &offset,
442 bool translucentBackground)
443{
444 if (!m_buffers.back()) {
445 qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first";
446 return FlushFailed;
447 }
448
449 finalizeBackBuffer();
450
451 return QPlatformBackingStore::rhiFlush(window, sourceDevicePixelRatio, region, offset, textures, translucentBackground);
452}
453
455{
456 if (!m_buffers.back())
457 return QImage();
458
459 const_cast<QCALayerBackingStore*>(this)->finalizeBackBuffer();
460
461 // We need to make a copy here, as the returned image could be used just
462 // for reading, in which case it won't detach, and then the underlying
463 // image data might change under the feet of the client when we re-use
464 // the buffer at a later point.
465 m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess);
466 QImage imageCopy = m_buffers.back()->asImage()->copy();
467 m_buffers.back()->unlock();
468 return imageCopy;
469}
470
471void QCALayerBackingStore::backingPropertiesChanged()
472{
473 // Ideally this would be plumbed from the platform layer to QtGui, and
474 // the QBackingStore would be recreated, but we don't have that code yet,
475 // so at least make sure we update our backingstore when the backing
476 // properties (color space e.g.) are changed.
477
478 Q_ASSERT(window()->handle());
479
480 qCDebug(lcQpaBackingStore) << "Backing properties for" << window() << "did change";
481
482 const auto newColorSpace = colorSpace();
483 qCDebug(lcQpaBackingStore) << "Updating color space of existing buffers to" << newColorSpace;
484 for (auto &buffer : m_buffers) {
485 if (buffer)
486 buffer->setColorSpace(newColorSpace);
487 }
488}
489
491{
492 return m_buffers.back().get();
493}
494
495void QCALayerBackingStore::updateDirtyStates(const QRegion &paintedRegion)
496{
497 // Update dirty state of buffers based on what was painted. The back buffer will be
498 // less dirty, since we painted to it, while other buffers will become more dirty.
499 // This allows us to minimize copies between front and back buffers on swap in the
500 // cases where the painted region overlaps with the previous frame (front buffer).
501 for (const auto &buffer : m_buffers) {
502 if (buffer == m_buffers.back())
503 buffer->dirtyRegion -= paintedRegion;
504 else
505 buffer->dirtyRegion += paintedRegion;
506 }
507}
508
509void QCALayerBackingStore::finalizeBackBuffer()
510{
511 // After painting, the back buffer is only guaranteed to have content for the painted
512 // region, and may still have dirty areas that need to be synced up with the front buffer,
513 // if we have one. We know that the front buffer is always up to date.
514
515 if (!m_buffers.back()->isDirty())
516 return;
517
518 qCDebug(lcQpaBackingStore) << "Finalizing back buffer with dirty region" << m_buffers.back()->dirtyRegion;
519
520 if (m_buffers.back() != m_buffers.front()) {
521 m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
522 blitBuffer(m_buffers.front().get(), m_buffers.back()->dirtyRegion, m_buffers.back().get());
523 m_buffers.back()->unlock();
524 } else {
525 qCDebug(lcQpaBackingStore) << "Front and back buffer is the same. Can not finalize back buffer.";
526 }
527
528 // The back buffer is now completely in sync, ready to be presented
529 m_buffers.back()->dirtyRegion = QRegion();
530}
531
532/*
533 \internal
534
535 Blits \a sourceRegion from \a sourceBuffer to \a destinationBuffer,
536 at offset \a destinationOffset.
537
538 The source buffer is automatically locked for read only access
539 during the blit.
540
541 The destination buffer has to be locked for write access by the
542 caller.
543*/
544
545void QCALayerBackingStore::blitBuffer(GraphicsBuffer *sourceBuffer, const QRegion &sourceRegion,
546 GraphicsBuffer *destinationBuffer, const QPoint &destinationOffset)
547{
548 Q_ASSERT(sourceBuffer && destinationBuffer);
549 Q_ASSERT(sourceBuffer != destinationBuffer);
550
551 if (sourceRegion.isEmpty())
552 return;
553
554 qCDebug(lcQpaBackingStore) << "Blitting" << sourceRegion << "of" << sourceBuffer
555 << "to" << sourceRegion.translated(destinationOffset) << "of" << destinationBuffer;
556
557 Q_ASSERT(destinationBuffer->isLocked() == QPlatformGraphicsBuffer::SWWriteAccess);
558
559 sourceBuffer->lock(QPlatformGraphicsBuffer::SWReadAccess);
560 const QImage *sourceImage = sourceBuffer->asImage();
561
562 const QRect sourceBufferBounds(QPoint(0, 0), sourceBuffer->size());
563 const qreal sourceDevicePixelRatio = sourceImage->devicePixelRatio();
564
565 QPainter painter(destinationBuffer->asImage());
567
568 // Let painter operate in device pixels, to make it easier to compare coordinates
569 const qreal destinationDevicePixelRatio = painter.device()->devicePixelRatio();
570 painter.scale(1.0 / destinationDevicePixelRatio, 1.0 / destinationDevicePixelRatio);
571
572 for (const QRect &rect : sourceRegion) {
573 QRect sourceRect(rect.topLeft() * sourceDevicePixelRatio,
574 rect.size() * sourceDevicePixelRatio);
575 QRect destinationRect((rect.topLeft() + destinationOffset) * destinationDevicePixelRatio,
576 rect.size() * destinationDevicePixelRatio);
577
578#ifdef QT_DEBUG
579 if (Q_UNLIKELY(!sourceBufferBounds.contains(sourceRect.bottomRight()))) {
580 qCWarning(lcQpaBackingStore) << "Source buffer of size" << sourceBuffer->size()
581 << "is too small to blit" << sourceRect;
582 }
583#endif
584 painter.drawImage(destinationRect, *sourceImage, sourceRect);
585 }
586
587 sourceBuffer->unlock();
588}
589
590// ----------------------------------------------------------------------------
591
592QCALayerBackingStore::GraphicsBuffer::GraphicsBuffer(const QSize &size, qreal devicePixelRatio,
593 const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace)
595 , dirtyRegion(QRect(QPoint(0, 0), size / devicePixelRatio))
596 , m_devicePixelRatio(devicePixelRatio)
597{
598 setColorSpace(colorSpace);
599}
600
601QRegion QCALayerBackingStore::GraphicsBuffer::validRegion() const
602{
603
604 QRegion fullRegion = QRect(QPoint(0, 0), size() / m_devicePixelRatio);
605 return fullRegion - dirtyRegion;
606}
607
608QImage *QCALayerBackingStore::GraphicsBuffer::asImage()
609{
610 if (m_image.isNull()) {
611 qCDebug(lcQpaBackingStore) << "Setting up paint device for" << this;
612 CFRetain(surface());
613 m_image = QImage(data(), size().width(), size().height(),
614 bytesPerLine(), QImage::toImageFormat(format()),
615 QImageCleanupFunction(CFRelease), surface());
616 m_image.setDevicePixelRatio(m_devicePixelRatio);
617 }
618
619 Q_ASSERT_X(m_image.constBits() == data(), "QCALayerBackingStore",
620 "IOSurfaces should have have a fixed location in memory once created");
621
622 return &m_image;
623}
624
626
627#include "moc_qcocoabackingstore.cpp"
QPaintDevice * paintDevice() override
Implement this function to return the appropriate paint device.
QImage toImage() const override
Implemented in subclasses to return the content of the backingstore as a QImage.
bool scroll(const QRegion &region, int dx, int dy) override
Scrolls the given area dx pixels to the right and dy downward; both dx and dy may be negative.
void endPaint() override
This function is called after painting onto the surface has ended.
void flush(QWindow *, const QRegion &, const QPoint &) override
Flushes the given region from the specified window.
void beginPaint(const QRegion &region) override
This function is called before painting onto the surface begins, with the region in which the paintin...
QPlatformGraphicsBuffer * graphicsBuffer() const override
Accessor for a backingstores graphics buffer abstraction.
QCALayerBackingStore(QWindow *window)
bool eventFilter(QObject *watched, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.
FlushResult rhiFlush(QWindow *window, qreal sourceDevicePixelRatio, const QRegion &region, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) override
Flushes the given region from the specified window, and compositing it with the specified textures li...
void resize(const QSize &size, const QRegion &staticContents) override
static QCFType constructFromGet(const T &t)
QCFType< CGColorSpaceRef > colorSpace() const
QCocoaBackingStore(QWindow *window)
NSView * view() const
qreal devicePixelRatio() const override
Reimplement this function in subclass to return the device pixel ratio for the window.
\inmodule QtCore
Definition qcoreevent.h:45
@ PlatformSurface
Definition qcoreevent.h:278
\inmodule QtGui
Definition qimage.h:37
static QPixelFormat toPixelFormat(QImage::Format format) noexcept
Converts format into a QPixelFormat.
Definition qimage.cpp:6392
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
static QImage::Format toImageFormat(QPixelFormat format) noexcept
Converts format into a QImage::Format.
Definition qimage.cpp:6401
\inmodule QtCore
Definition qobject.h:103
void installEventFilter(QObject *filterObj)
Installs an event filter filterObj on this object.
Definition qobject.cpp:2339
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
qreal devicePixelRatio() const
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
QPaintDevice * device() const
Returns the paint device on which this painter is currently painting, or \nullptr if the painter is n...
void scale(qreal sx, qreal sy)
Scales the coordinate system by ({sx}, {sy}).
void setCompositionMode(CompositionMode mode)
Sets the composition mode to the given mode.
void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect, Qt::ImageConversionFlags flags=Qt::AutoColor)
Draws the rectangular portion source of the given image into the target rectangle in the paint device...
bool end()
Ends painting.
@ CompositionMode_Source
Definition qpainter.h:101
void fillRect(const QRectF &, const QBrush &)
Fills the given rectangle with the brush specified.
\inmodule QtGui
The QPlatformBackingStore class provides the drawing area for top-level windows.
QWindow * window() const
Returns a pointer to the top-level window associated with this surface.
virtual FlushResult rhiFlush(QWindow *window, qreal sourceDevicePixelRatio, const QRegion &region, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground)
Flushes the given region from the specified window, and compositing it with the specified textures li...
QSize size() const
Accessor for content size.
The QPlatformSurfaceEvent class is used to notify about native platform surface events....
Definition qevent.h:531
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:242
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
bool isEmpty() const
Returns true if the region is empty; otherwise returns false.
QRegion translated(int dx, int dy) const
Definition qregion.cpp:593
QRect geometry
the screen's geometry in pixels
Definition qscreen.h:45
\inmodule QtCore
Definition qsize.h:25
constexpr QSize boundedTo(const QSize &) const noexcept
Returns a size holding the minimum width and height of this size and the given otherSize.
Definition qsize.h:197
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
SwapBehavior swapBehavior() const
Returns the configured swap behaviour.
\inmodule QtGui
Definition qwindow.h:63
QSurfaceFormat format() const override
Returns the actual format of this window.
Definition qwindow.cpp:946
rect
[4]
Combined button and popup list for selecting options.
@ transparent
Definition qnamespace.h:47
QNSView * qnsview_cast(NSView *view)
Returns the view cast to a QNSview if possible.
#define Q_UNLIKELY(x)
void(* QImageCleanupFunction)(void *)
Definition qimage.h:34
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr quint32 qNextPowerOfTwo(quint32 v)
Definition qmath.h:335
GLuint64 GLenum void * handle
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint const GLuint GLuint const GLuint * textures
GLenum GLuint id
[7]
GLuint object
[3]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLint GLsizei width
GLenum GLuint GLintptr offset
GLint GLsizei GLsizei GLenum format
struct _cl_event * event
GLenum GLenum GLenum GLenum GLenum scale
void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset)
static bool hasAlpha(const QImage &image)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
QPainter painter(this)
[7]
aWidget window() -> setWindowTitle("New Window Title")
[2]
QQuickView * view
[0]