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来对照下
枚举类型 | 枚举类型 | 描述 |
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 < 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
继承自QPaintEngineEx
,QPaintEngineEx
继承自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一样,也会有很多种类型的派生。
- 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 <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 ®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<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 ®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()));
以上是关于QT 绘画引擎之qtwayland的主要内容,如果未能解决你的问题,请参考以下文章