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 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继承自QPaintEngineExQPaintEngineEx继承自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一样,也会有很多种类型的派生。

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