QT 绘画引擎之qtwayland

Posted dongfangxingyu1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT 绘画引擎之qtwayland相关的知识,希望对你有一定的参考价值。

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来对照下

枚举类型枚举类型描述
ConstantValueDescription
QPaintEngine::X110
QPaintEngine::Windows1
QPaintEngine::MacPrinter4
QPaintEngine::CoreGraphics3macOS的Quartz2D(CoreGraphics)
QPaintEngine::QWindowSystem2嵌入式Linux的Qt
QPaintEngine::PostScript6(不再支持)
QPaintEngine::OpenGL7
QPaintEngine::Picture8QPicture 格式
QPaintEngine::SVG9可伸缩矢量图形XML格式
QPaintEngine::Raster10
QPaintEngine::Direct3D11仅Windows,基于Direct3D的引擎
QPaintEngine::Pdf12PDF格式
QPaintEngine::OpenVG13
QPaintEngine::User50
QPaintEngine::MaxUser100
QPaintEngine::OpenGL214
QPaintEngine::PaintBuffer15
QPaintEngine::Blitter16
QPaintEngine::Direct2D17仅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 < lineCount; ++i) 
                QLineF line = lines[i];
                line.translate(d->state->matrix.dx(), d->state->matrix.dy());
                d->engine->drawLines(&line, 1); //这里调用qpaintengine
            
         else 
            QPainterPath linePath;
            for (int i = 0; i < lineCount; ++i) 
                linePath.moveTo(lines[i].p1());
                linePath.lineTo(lines[i].p2());
            
            d->draw_helper(linePath, QPainterPrivate::StrokeDraw); //这里会走模拟绘制本质上也会走一个engine
        
        return;
    
    d->engine->drawLines(lines, lineCount); //或者这里调用qpaintengine

那么就调用到了QPaintEngineRaster的相关实现。QRasterPaintEngine继承自QPaintEngineExQPaintEngineEx继承自QPaintEngine

void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)

#ifdef QT_DEBUG_DRAW
    qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
#endif
    Q_D(QRasterPaintEngine);
    QRasterPaintEngineState *s = state();

    ensurePen();
    if (!s->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继承自QPaintDevice
QPainter painter(this);

QWidget继承自QPaintDevice,看下源码实现

QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f)
    : QObject(dd, nullptr), QPaintDevice()

QPaintDevice本质上就是一个绘制设备,供我们使用,由于QPaintDevice创建QPaintEngine ,所以QPaintDevice跟QPaintEngine一样,也会有很多种类型的派生。

  1. QGLFramebufferObject
  2. QGLPixelBuffer
  3. QImage,
  4. QOpenGLPaintDevice,
  5. QPagedPaintDevice
  6. QPaintDeviceWindow,
  7. QPicture
  8. QPixmap,
  9. QSvgGenerator
  10. QWidget

3.QWidget  显示流程:

3.11.update()

void QWidget::update()

    update(rect());

 
void QWidget::update(const QRect &rect)

    Q_D(QWidget);
    d->update(rect);

 
template <typename T>
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" << updateTime;
    Q_ASSERT(tlw->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<QRect>(const QRect &, QWidget *, UpdateTime, BufferState);
template void QWidgetRepaintManager::markDirty<QRegion>(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.void repaint();

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.void scroll(int dx, int dy);

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<QRect> 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) << "Syncing dirty widgets";

    updateRequestSent = false;
    if (qt_widget_private(tlw)->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" << dirtyWidgets;

    const bool updatesDisabled = !tlw->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 < dirtyWidgets.size(); ++i)
                resetWidget(dirtyWidgets.at(i));
            dirtyWidgets.clear();
            repaintAllWidgets = true;
        
    

    if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size())
        store->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<QWidget *, 32> opaqueNonOverlappedWidgets;
    for (int i = 0; i < dirtyWidgets.size(); ++i) 
        QWidget *w = dirtyWidgets.at(i);
        QWidgetPrivate *wd = w->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<QWidget *, 16> 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 < numPaintPending; ++i) 
            QWidget *w = paintPending[i];
            w->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 < tl->count(); ++i) 
            QWidget *w = static_cast<QWidget *>(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 < dirtyRenderToTextureWidgets.count(); ++i)
        resetWidget(dirtyRenderToTextureWidgets.at(i));
    dirtyRenderToTextureWidgets.clear();
#endif

#if QT_CONFIG(graphicsview)
    if (tlw->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 < opaqueNonOverlappedWidgets.size(); ++i) 
        QWidget *w = opaqueNonOverlappedWidgets[i];
        QWidgetPrivate *wd = w->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 &region)

    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<QImage *>(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" << d_ptr->window;
            qCDebug(lcScaling) << "  source size" << source->size() << "dpr" << source->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" << d_ptr->highDpiBackingstore->size()
                               << "dpr" << targetDevicePixelRatio;
        
    



void QBackingStore::endPaint()

    if (paintDevice()->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 &region)

    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 &region, 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()));

以上是关于QT 绘画引擎之qtwayland的主要内容,如果未能解决你的问题,请参考以下文章

在 Qt 小部件中的视频顶部绘画

Qt 5.3。在 QOpenGLFrameBuffer 上绘画

Qt:优化绘画事件

QT 绘画到小部件

Qt 通过绘画设置边框阴影

Qt 之 QPixmap