1 . qt 绘画介绍
QPaintEngine,QPainter,QPaintDevice组成了Qt绘制界面的基础。QPainter为开发者提供外部接口方法用于绘制。QPaintEngine为QPainter提供一些绘制的具体实现 。QPaintDevice为QPainter提供一个绘图设备,用于显示亦或储存。 如果你想使用QPainter绘制自定义的后端(译者注:这里可以理解为QPaintDevice)。你可以继承QPaintEngine,并实现其所有的虚函数。然后子类化QPaintDevice并且实现它的纯虚成员函数(QPaintDevice::paintEngine())。
现在QPaintEngine主要提供的是Qt自带的光栅化引擎(raster engine),Qt在他所有支持的平台上,提供了一个功能完备的光栅化引擎。在Windows, X11 和 macOS平台上,Qt自带的光栅化引擎都是QWidget这个基础类的默认的绘制方法的提供者,亦或是QImage的绘制方法的提供者。当然有一些特殊的绘制设备的绘制引擎不提供对应的绘制方法,这时候就会调用默认的光栅化引擎。当然,我们也为OpenGL(可通过QOpenGLWidget访问)跟打印(允许QPainter在QPrinter对象上绘制,用于生成pdf之类的)也提供了对应的QPaintEngine的实现
find . -name qpaintengine*.cpp
或者你在windows上用everything搜一下
./5.15.2/Src/qtbase/src/gui/image/qpaintengine_pic.cpp
./5.15.2/Src/qtbase/src/gui/painting/qpaintengine.cpp
./5.15.2/Src/qtbase/src/gui/painting/qpaintengineex.cpp
./5.15.2/Src/qtbase/src/gui/painting/qpaintengine_blitter.cpp
./5.15.2/Src/qtbase/src/gui/painting/qpaintengine_raster.cpp
./5.15.2/Src/qtbase/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
./5.15.2/Src/qtbase/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
./5.15.2/Src/qtbase/src/printsupport/kernel/qpaintengine_alpha.cpp
./5.15.2/Src/qtbase/src/printsupport/kernel/qpaintengine_preview.cpp
这些都是QPaintEngine在各个不同端的派生,有兴趣可以搜下qpaintdevice相关的,也差不多都是这样。比如qpaintengine_raster.cpp 就是Qt自己的光栅化引擎实现,qpaintengine_x11.cpp就是在Linux下默认跟x11交互的光栅化实现。当然并不是所有的派生都会有自己独立的cpp文件,或者叫相关的cpp 。可以对比Qt的官方API来对照下
枚举类型 | 枚举类型 | 描述 |
Constant | Value | Description |
QPaintEngine::X11 | 0 | |
QPaintEngine::Windows | 1 | |
QPaintEngine::MacPrinter | 4 | |
QPaintEngine::CoreGraphics | 3 | macOS的Quartz2D(CoreGraphics) |
QPaintEngine::QWindowSystem | 2 | 嵌入式Linux的Qt |
QPaintEngine::PostScript | 6 | (不再支持) |
QPaintEngine::OpenGL | 7 | |
QPaintEngine::Picture | 8 | QPicture 格式 |
QPaintEngine::SVG | 9 | 可伸缩矢量图形XML格式 |
QPaintEngine::Raster | 10 | |
QPaintEngine::Direct3D | 11 | 仅Windows,基于Direct3D的引擎 |
QPaintEngine::Pdf | 12 | PDF格式 |
QPaintEngine::OpenVG | 13 | |
QPaintEngine::User | 50 | |
QPaintEngine::MaxUser | 100 | |
QPaintEngine::OpenGL2 | 14 | |
QPaintEngine::PaintBuffer | 15 | |
QPaintEngine::Blitter | 16 | |
QPaintEngine::Direct2D | 17 | 仅Windows,基于Direct2D的引擎 |
2. 说下Qt绘制一条线的流程
现在有这样的代码,我们来在Qt中绘制一条线
QLineF line(10.0, 80.0, 90.0, 20.0);QPainter painter(this);painter.drawLine(line);
如果我们的Qt把渲染引擎设置成了raster引擎,那么qpainter
的实现本质上是调用的QPaintEngine的相关代码。
void QPainter::drawLines(const QLineF *lines, int lineCount){//此处精简代码 xxxxxxxx if (lineEmulation) { if (lineEmulation == QPaintEngine::PrimitiveTransform && d->state->matrix.type() == QTransform::TxTranslate) { for (int i = 0; i state->matrix.dx(), d->state->matrix.dy()); d->engine->drawLines(&line, 1); //这里调用qpaintengine } } else { QPainterPath linePath; for (int i = 0; i draw_helper(linePath, QPainterPrivate::StrokeDraw); //这里会走模拟绘制本质上也会走一个engine } return; } d->engine->drawLines(lines, lineCount); //或者这里调用qpaintengine}
那么就调用到了QPaintEngineRaster
的相关实现。QRasterPaintEngine
继承自QPaintEngineEx
,QPaintEngineEx
继承自QPaintEngine
void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount){#ifdef QT_DEBUG_DRAW qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" <penData.blend) return; if (s->flags.fast_pen) { QCosmeticStroker stroker(s, d->deviceRect, d->deviceRectUnclipped); stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding); for (int i=0; i<lineCount; ++i) { const QLine &l = lines[i]; stroker.drawLine(l.p1(), l.p2()); } } else { QPaintEngineEx::drawLines(lines, lineCount); }}
所以QPainter在画画的时候本质上是QPaintEngine提供的方法。
关于QPaintDevice
由QPaintDevice创建QPaintEngine,并维护其生命周期,
上面的代码中,是这样初始化QPainter的。
我们一般重写一个QWidget的paintevent的时候才会这样。
//这里的this实际上就是一个QWidget,QWidget继承自QPaintDeviceQPainter painter(this);
QWidget继承自QPaintDevice,看下源码实现
QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f) : QObject(dd, nullptr), QPaintDevice()
QPaintDevice本质上就是一个绘制设备,供我们使用,由于QPaintDevice创建QPaintEngine ,所以QPaintDevice跟QPaintEngine一样,也会有很多种类型的派生。
- QGLFramebufferObject
- QGLPixelBuffer
- QImage,
- QOpenGLPaintDevice,
- QPagedPaintDevice
- QPaintDeviceWindow,
- QPicture
- QPixmap,
- QSvgGenerator
- QWidget
3.QWidget 显示流程:
3.11.update()
void QWidget::update(){ update(rect());} void QWidget::update(const QRect &rect){ Q_D(QWidget); d->update(rect);} template void QWidgetPrivate::update(T r){ Q_Q(QWidget); if (!q->isVisible() || !q->updatesEnabled()) return; T clipped = r & q->rect(); if (clipped.isEmpty()) return; if (q->testAttribute(Qt::WA_WState_InPaintEvent)) { QCoreApplication::postEvent(q, new QUpdateLaterEvent(clipped)); return; } QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); if (tlwExtra && tlwExtra->backingStore) tlwExtra->repaintManager->markDirty(clipped, q);} void QWidgetRepaintManager::markDirty(const T &r, QWidget *widget, UpdateTime updateTime, BufferState bufferState){ qCInfo(lcWidgetPainting) << "Marking" << r << "of" << widget << "dirty" << "with" <d_func()->extra); Q_ASSERT(tlw->d_func()->extra->topextra); Q_ASSERT(widget->isVisible() && widget->updatesEnabled()); Q_ASSERT(widget->window() == tlw); Q_ASSERT(!r.isEmpty());#if QT_CONFIG(graphicseffect) widget->d_func()->invalidateGraphicsEffectsRecursively();#endif QRect widgetRect = widgetRectFor(widget, r); // --------------------------------------------------------------------------- if (widget->d_func()->shouldPaintOnScreen()) { if (widget->d_func()->dirty.isEmpty()) { widget->d_func()->dirty = r; sendUpdateRequest(widget, updateTime); return; } else if (qt_region_strictContains(widget->d_func()->dirty, widgetRect)) { if (updateTime == UpdateNow) sendUpdateRequest(widget, updateTime); return; // Already dirty } const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty(); widget->d_func()->dirty += r; if (!eventAlreadyPosted || updateTime == UpdateNow) sendUpdateRequest(widget, updateTime); return; } // --------------------------------------------------------------------------- if (QWidgetPrivate::get(widget)->renderToTexture) { if (!widget->d_func()->inDirtyList) addDirtyRenderToTextureWidget(widget); if (!updateRequestSent || updateTime == UpdateNow) sendUpdateRequest(tlw, updateTime); return; } // --------------------------------------------------------------------------- QRect effectiveWidgetRect = widget->d_func()->effectiveRectFor(widgetRect); const QPoint offset = widget->mapTo(tlw, QPoint()); QRect translatedRect = effectiveWidgetRect.translated(offset);#if QT_CONFIG(graphicseffect) // Graphics effects may exceed window size, clamp translatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size()));#endif if (qt_region_strictContains(dirty, translatedRect)) { if (updateTime == UpdateNow) sendUpdateRequest(tlw, updateTime); return; // Already dirty } // --------------------------------------------------------------------------- if (bufferState == BufferInvalid) { const bool eventAlreadyPosted = !dirty.isEmpty() || updateRequestSent;#if QT_CONFIG(graphicseffect) if (widget->d_func()->graphicsEffect) dirty += widget->d_func()->effectiveRectFor(r).translated(offset); else#endif dirty += r.translated(offset); if (!eventAlreadyPosted || updateTime == UpdateNow) sendUpdateRequest(tlw, updateTime); return; } // --------------------------------------------------------------------------- if (dirtyWidgets.isEmpty()) { addDirtyWidget(widget, r); sendUpdateRequest(tlw, updateTime); return; } // --------------------------------------------------------------------------- if (widget->d_func()->inDirtyList) { if (!qt_region_strictContains(widget->d_func()->dirty, effectiveWidgetRect)) {#if QT_CONFIG(graphicseffect) if (widget->d_func()->graphicsEffect) widget->d_func()->dirty += widget->d_func()->effectiveRectFor(r); else#endif widget->d_func()->dirty += r; } } else { addDirtyWidget(widget, r); } // --------------------------------------------------------------------------- if (updateTime == UpdateNow) sendUpdateRequest(tlw, updateTime);}template void QWidgetRepaintManager::markDirty(const QRect &, QWidget *, UpdateTime, BufferState);template void QWidgetRepaintManager::markDirty(const QRegion &, QWidget *, UpdateTime, BufferState);void QWidgetRepaintManager::addDirtyWidget(QWidget *widget, const QRegion &rgn){ if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { QWidgetPrivate *widgetPrivate = widget->d_func();#if QT_CONFIG(graphicseffect) if (widgetPrivate->graphicsEffect) widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect()); else#endif // QT_CONFIG(graphicseffect) widgetPrivate->dirty = rgn; dirtyWidgets.append(widget); widgetPrivate->inDirtyList = true; }}
update()函数并不立即执行刷新。
3.2.voidrepaint();
void QWidget::repaint(){ repaint(rect());} void QWidget::repaint(const QRect &rect){ Q_D(QWidget); d->repaint(rect);} void QWidgetPrivate::repaint(T r){ Q_Q(QWidget); if (!q->isVisible() || !q->updatesEnabled() || r.isEmpty()) return; QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); if (tlwExtra && tlwExtra->backingStore) tlwExtra->repaintManager->markDirty(r, q, QWidgetRepaintManager::UpdateNow);}
repaint()函数为立即刷新。
3.3.voidscroll(intdx,intdy);
void QWidget::scroll(int dx, int dy){ if ((!updatesEnabled() && children().size() == 0) || !isVisible()) return; if (dx == 0 && dy == 0) return; Q_D(QWidget);#if QT_CONFIG(graphicsview) if (QGraphicsProxyWidget *proxy = QWidgetPrivate::nearestGraphicsProxyWidget(this)) { // Graphics View maintains its own dirty region as a list of rects; // until we can connect item updates directly to the view, we must // separately add a translated dirty region. for (const QRect &rect : d->dirty) proxy->update(rect.translated(dx, dy)); proxy->scroll(dx, dy, proxy->subWidgetRect(this)); return; }#endif d->setDirtyOpaqueRegion(); d->scroll_sys(dx, dy);} void QWidgetPrivate::scroll_sys(int dx, int dy){ Q_Q(QWidget); scrollChildren(dx, dy); scrollRect(q->rect(), dx, dy);} void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy){ Q_Q(QWidget); QWidget *tlw = q->window(); QTLWExtra* x = tlw->d_func()->topData(); QWidgetRepaintManager *repaintManager = x->repaintManager.get(); if (!repaintManager) return; static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0; const QRect clipR = clipRect(); const QRect scrollRect = rect & clipR; const bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent); if (!accelerateScroll) { if (!overlappedRegion(scrollRect.translated(data.crect.topLeft()), true).isEmpty()) { QRegion region(scrollRect); subtractOpaqueSiblings(region); invalidateBackingStore(region); }else { invalidateBackingStore(scrollRect); } } else { const QPoint toplevelOffset = q->mapTo(tlw, QPoint()); const QRect destRect = scrollRect.translated(dx, dy) & scrollRect; const QRect sourceRect = destRect.translated(-dx, -dy); const QRegion overlappedExpose = (overlappedRegion(scrollRect.translated(data.crect.topLeft()))) .translated(-data.crect.topLeft()) & clipR; QRegion childExpose(scrollRect); const qreal factor = QHighDpiScaling::factor(q->windowHandle()); if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { const QList rectsToScroll = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy); for (const QRect &r : rectsToScroll) { if (repaintManager->bltRect(r, dx, dy, q)) { childExpose -= r.translated(dx, dy); } } } childExpose -= overlappedExpose; if (inDirtyList) { if (rect == q->rect()) { dirty.translate(dx, dy); } else { QRegion dirtyScrollRegion = dirty.intersected(scrollRect); if (!dirtyScrollRegion.isEmpty()) { dirty -= dirtyScrollRegion; dirtyScrollRegion.translate(dx, dy); dirty += dirtyScrollRegion; } } } if (!q->updatesEnabled()) return; if (!overlappedExpose.isEmpty()) invalidateBackingStore(overlappedExpose); if (!childExpose.isEmpty()) { repaintManager->markDirty(childExpose, q); isScrolled = true; } // Instead of using native scroll-on-screen, we copy from // backingstore, giving only one screen update for each // scroll, and a solid appearance repaintManager->markNeedsFlush(q, destRect, toplevelOffset); }}
3.4 图像数据刷入底层
bool QWidget::event(QEvent *event){ case QEvent::UpdateRequest: d->syncBackingStore(); break; }void QWidgetPrivate::syncBackingStore(){ if (shouldPaintOnScreen()) { paintOnScreen(dirty); dirty = QRegion(); } else if (QWidgetRepaintManager *repaintManager = maybeRepaintManager()) { repaintManager->sync(); }}void QWidgetRepaintManager::sync(){ qCInfo(lcWidgetPainting) <shouldDiscardSyncRequest()) { // If the top-level is minimized, it's not visible on the screen so we can delay the // update until it's shown again. In order to do that we must keep the dirty states. // These will be cleared when we receive the first expose after showNormal(). // However, if the widget is not visible (isVisible() returns false), everything will // be invalidated once the widget is shown again, so clear all dirty states. if (!tlw->isVisible()) { dirty = QRegion(); for (int i = 0; i < dirtyWidgets.size(); ++i) resetWidget(dirtyWidgets.at(i)); dirtyWidgets.clear(); } return; } if (syncAllowed()) paintAndFlush();}void QWidgetRepaintManager::paintAndFlush(){ qCInfo(lcWidgetPainting) << "Painting and flushing dirty" << "top level" << dirty << "and dirty widgets" <updatesEnabled(); bool repaintAllWidgets = false; const bool inTopLevelResize = tlw->d_func()->maybeTopData()->inTopLevelResize; const QRect tlwRect = tlw->data->crect; const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { if (hasStaticContents() && !store->size().isEmpty() ) { // Repaint existing dirty area and newly visible area. const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); const QRegion staticRegion(staticContents(0, clipRect)); QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height()); newVisible -= staticRegion; dirty += newVisible; store->setStaticContents(staticRegion); } else { // Repaint everything. dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height()); for (int i = 0; i resize(tlwRect.size()); if (updatesDisabled) return; // Contains everything that needs repaint. QRegion toClean(dirty); // Loop through all update() widgets and remove them from the list before they are // painted (in case someone calls update() in paintEvent). If the widget is opaque // and does not have transparent overlapping siblings, append it to the // opaqueNonOverlappedWidgets list and paint it directly without composition. QVarLengthArray opaqueNonOverlappedWidgets; for (int i = 0; i d_func(); if (wd->data.in_destructor) continue; // Clip with mask() and clipRect(). wd->dirty &= wd->clipRect(); wd->clipToEffectiveMask(wd->dirty); // Subtract opaque siblings and children. bool hasDirtySiblingsAbove = false; // We know for sure that the widget isn't overlapped if 'isMoved' is true. if (!wd->isMoved) wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove); // Make a copy of the widget's dirty region, to restore it in case there is an opaque // render-to-texture child that completely covers the widget, because otherwise the // render-to-texture child won't be visible, due to its parent widget not being redrawn // with a proper blending mask. const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty; // Scrolled and moved widgets must draw all children. if (!wd->isScrolled && !wd->isMoved) wd->subtractOpaqueChildren(wd->dirty, w->rect()); if (wd->dirty.isEmpty() && wd->textureChildSeen) wd->dirty = dirtyBeforeSubtractedOpaqueChildren; if (wd->dirty.isEmpty()) { resetWidget(w); continue; } const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint())) : wd->dirty); toClean += widgetDirty;#if QT_CONFIG(graphicsview) if (tlw->d_func()->extra->proxyWidget) { resetWidget(w); continue; }#endif if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) { opaqueNonOverlappedWidgets.append(w); } else { resetWidget(w); dirty += widgetDirty; } } dirtyWidgets.clear();#ifndef QT_NO_OPENGL // Find all render-to-texture child widgets (including self). // The search is cut at native widget boundaries, meaning that each native child widget // has its own list for the subtree below it. QTLWExtra *tlwExtra = tlw->d_func()->topData(); tlwExtra->widgetTextures.clear(); findAllTextureWidgetsRecursively(tlw, tlw); qt_window_private(tlw->windowHandle())->compositing = false; // will get updated in flush()#endif if (toClean.isEmpty()) { // Nothing to repaint. However renderToTexture widgets are handled // specially, they are not in the regular dirty list, in order to // prevent triggering unnecessary backingstore painting when only the // OpenGL content changes. Check if we have such widgets in the special // dirty list. QVarLengthArray paintPending; const int numPaintPending = dirtyRenderToTextureWidgets.count(); paintPending.reserve(numPaintPending); for (int i = 0; i < numPaintPending; ++i) { QWidget *w = dirtyRenderToTextureWidgets.at(i); paintPending << w; resetWidget(w); } dirtyRenderToTextureWidgets.clear(); for (int i = 0; i d_func()->sendPaintEvent(w->rect()); if (w != tlw) { QWidget *npw = w->nativeParentWidget(); if (hasPlatformWindow(w) || (npw && npw != tlw)) { if (!hasPlatformWindow(w)) w = npw; markNeedsFlush(w); } } } // We might have newly exposed areas on the screen if this function was // called from sync(QWidget *, QRegion)), so we have to make sure those // are flushed. We also need to composite the renderToTexture widgets. flush(); return; }#ifndef QT_NO_OPENGL for (const auto &tl : tlwExtra->widgetTextures) { for (int i = 0; i count(); ++i) { QWidget *w = static_cast(tl->source(i)); if (dirtyRenderToTextureWidgets.contains(w)) { const QRect rect = tl->geometry(i); // mapped to the tlw already // Set a flag to indicate that the paint event for this // render-to-texture widget must not to be optimized away. w->d_func()->renderToTextureReallyDirty = 1; dirty += rect; toClean += rect; } } } for (int i = 0; i d_func()->extra->proxyWidget) { updateStaticContentsSize(); dirty = QRegion(); updateRequestSent = false; for (const QRect &rect : toClean) tlw->d_func()->extra->proxyWidget->update(rect); return; }#endif store->beginPaint(toClean); // Must do this before sending any paint events because // the size may change in the paint event. updateStaticContentsSize(); const QRegion dirtyCopy(dirty); dirty = QRegion(); updateRequestSent = false; // Paint opaque non overlapped widgets. for (int i = 0; i d_func(); QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawRecursive; // Scrolled and moved widgets must draw all children. if (!wd->isScrolled && !wd->isMoved) flags |= QWidgetPrivate::DontDrawOpaqueChildren; if (w == tlw) flags |= QWidgetPrivate::DrawAsRoot; QRegion toBePainted(wd->dirty); resetWidget(w); QPoint offset; if (w != tlw) offset += w->mapTo(tlw, QPoint()); wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, 0, this); } // Paint the rest with composition. if (repaintAllWidgets || !dirtyCopy.isEmpty()) { QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive; tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags, 0, this); } store->endPaint(); flush();}
store->beginPaint(toClean);
store->endPaint();
void QBackingStore::beginPaint(const QRegion ®ion){ if (d_ptr->highDpiBackingstore && d_ptr->highDpiBackingstore->devicePixelRatio() != d_ptr->window->devicePixelRatio()) resize(size()); QPlatformBackingStore *platformBackingStore = handle(); platformBackingStore->beginPaint(QHighDpi::toNativeLocalRegion(region, d_ptr->window)); // When QtGui is applying a high-dpi scale factor the backing store // creates a "large" backing store image. This image needs to be // painted on as a high-dpi image, which is done by setting // devicePixelRatio. Do this on a separate image instance that shares // the image data to avoid having the new devicePixelRatio be propagated // back to the platform plugin. QPaintDevice *device = platformBackingStore->paintDevice(); if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image) { QImage *source = static_cast(device); const bool needsNewImage = d_ptr->highDpiBackingstore.isNull() || source->data_ptr() != d_ptr->highDpiBackingstore->data_ptr() || source->size() != d_ptr->highDpiBackingstore->size() || source->devicePixelRatio() != d_ptr->highDpiBackingstore->devicePixelRatio(); if (needsNewImage) { qCDebug(lcScaling) << "QBackingStore::beginPaint new backingstore for" <window; qCDebug(lcScaling) << " source size" <size() << "dpr" <devicePixelRatio(); d_ptr->highDpiBackingstore.reset( new QImage(source->bits(), source->width(), source->height(), source->bytesPerLine(), source->format())); qreal targetDevicePixelRatio = d_ptr->window->devicePixelRatio(); d_ptr->highDpiBackingstore->setDevicePixelRatio(targetDevicePixelRatio); qCDebug(lcScaling) <<" destination size" <highDpiBackingstore->size() << "dpr" <paintingActive()) qWarning("QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?"); handle()->endPaint();}
QBackingStore 调用 QPlatformBackingStore 类。QPlatformBackingStore 是QPA 接口,由个backend 实现。
4 .QT wayland 实现QPlatformBackingStore 接口,将绘图数据提交到shm 中。
void QWaylandShmBackingStore::beginPaint(const QRegion ®ion){ mPainting = true; ensureSize(); waylandWindow()->setCanResize(false); if (mBackBuffer->image()->hasAlphaChannel()) { QPainter p(paintDevice()); p.setCompositionMode(QPainter::CompositionMode_Source); const QColor blank = Qt::transparent; for (const QRect &rect : region) p.fillRect(rect, blank); }}void QWaylandShmBackingStore::endPaint(){ mPainting = false; if (mPendingFlush) flush(window(), mPendingRegion, QPoint()); waylandWindow()->setCanResize(true);}void QWaylandShmBackingStore::ensureSize(){ waylandWindow()->setBackingStore(this); waylandWindow()->createDecoration(); resize(mRequestedSize);}void QWaylandShmBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset){ // Invoked when the window is of type RasterSurface or when the window is // RasterGLSurface and there are no child widgets requiring OpenGL composition. // For the case of RasterGLSurface + having to compose, the composeAndFlush() is // called instead. The default implementation from QPlatformBackingStore is sufficient // however so no need to reimplement that. Q_UNUSED(window); Q_UNUSED(offset); if (mPainting) { mPendingRegion |= region; mPendingFlush = true; return; } mPendingFlush = false; mPendingRegion = QRegion(); if (windowDecoration() && windowDecoration()->isDirty()) updateDecorations(); mFrontBuffer = mBackBuffer; QMargins margins = windowDecorationMargins(); waylandWindow()->safeCommit(mFrontBuffer, region.translated(margins.left(), margins.top()));}