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)
{
//此处精简代码xxxxxxxxif (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_DRAWqDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount;
#endifQ_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();
#endifQRect 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, clamptranslatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size()));
#endifif (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
#endifdirty += 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
#endifwidget->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;}
#endifd->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 appearancerepaintManager->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;}
#endifif (!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()
#endifif (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_OPENGLfor (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;}
#endifstore->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相关推荐

  1. qt绘画扇形drawPie()和反着三角函数qAcos()

    简介: qt绘画扇形drawPie(),绘画出弧线: 和由三角形的长度计算出角度,利用反三角函数qAcos() 文章目录 同步博文: 问题背景: 解决方法: 绘画扇形drawPie(): 反三角计算角 ...

  2. Qt 绘画小软件(功能完善,免费下载)

    Qt 绘画小软件(功能完善,免费下载) 源码下载 免费下载,看评论 操作说明 快捷键 Ctrl + C 复制选中区域快捷键 Ctrl + V 粘贴选中区域快捷键 Ctrl + X 剪切选中区域快捷键 ...

  3. qt绘画事件-设置背景图片

    前言 设置widget的背景图片,并在背景图片上放置别的图片 直接上代码: 头文件 void paintEvent(QPaintEvent *); cpp文件: void MainWindow::pa ...

  4. 零基础入门 Stable Diffusion - 无需显卡把 AI 绘画引擎搬进家用电脑

    我从小特别羡慕会画画的伙伴.他们能够将心中的想法画出来,而我最高水平的肖像画是丁老头.但在接触 Stable Diffusion 之后,我感觉自己脱胎换骨,给自己贴上了「会画画」的新标签. 丁老头进化 ...

  5. 【Qt象棋游戏】02_绘画象棋棋盘

    文章目录 01 - 相关成员与方法 02 - 棋盘颜色 03 - 绘画棋盘线 04 - 添加"井"字格 05 - 总结 01 - 相关成员与方法   进行代码编写之前,在chess ...

  6. Qt实现Word文档界面样式--QtitanRibbon

    QtitanRibbon - Microsoft Ribbon for Nokia's Qt概念的实现 QtitanRibbon组件实现了Microsoft Ribbon for Qt的概念,设计用于 ...

  7. 绘画系统(02):【纲】Paint Devices and Backends[官翻]

    绘画设备和后端 Paint Devices and Backends 创建绘画设备 ​ QPaintDevice类是可以绘制的对象的基类,即QPainter可以在任何QPaintDevice子类上绘制 ...

  8. Qt 与 Qt Creator 简介

    该文章原创于Qter开源社区(www.qter.org),作者 yafeilinux,转载请注明出处! 一.Qt 与 Qt Creator 简介 Qt 是一个跨平台应用程序和 UI 开发框架.使用 Q ...

  9. 【Qt】QPaintDevice类详解

    00. 目录 文章目录 00. 目录 01. 概述 02. 绘制设备参数的指标 03. 成员方法 04. 预留 05. 预留 06. 附录 01. 概述 绘画设备是二维空间的抽象,可以使用QPaint ...

最新文章

  1. js中常用的对象—String的属性和方法
  2. 学猫叫用计算机歌词,我们一起学猫叫什么歌歌词是什么意思
  3. 台式计算机风扇一直响,风扇一直响的原因是什么? 处理方法
  4. PyTorch Upsample() 函数实现上采样
  5. MYSQL二级表的管理_MySQL库和表的管理
  6. 模型开发-GBDT决策树模型开发代码
  7. goods.java_javaweb网上书城项目 1.用户管理:注册会员 - 下载 - 搜珍网
  8. Vijos——T 1629 八
  9. oracle中查询某张表的大小
  10. html5+中奖结果页面,html5+css3实现抽奖活动的效果
  11. Python数据可视化案例二:动态更新数据
  12. python面向对象(其二)
  13. C# 滑块长度确认 Scrollbar滑块长度问题 水平垂直滚动条滑块高度宽度问题
  14. Python设计模式:适配器模式
  15. win10系统realtek高清晰音频管理器有什么用
  16. JS判断数组是否相同
  17. qq连连看看外挂-我的QQ连连看“辅助”程序源码
  18. 中国行政区划编码-省市县镇村
  19. 支付宝门店码,全面助力商家快速实现数字化经营
  20. 投递邮件简历要注意的细节

热门文章

  1. sharepoint2007就地升级2010系列(四)升级数据库
  2. 服务器水厂物资管理系统,基于B/S的水厂管理信息系统的设计与实现
  3. 2017第八届蓝桥杯省赛JAVA C组真题解析(带源码及解析)
  4. QQ空间打不开怎么办
  5. jQuery(一)jQuery概述、使用方式、原理、查找元素
  6. 齐岳离子液体[C1MIm]SbF6/cas:885624-41-9/1,3-二甲基咪唑六氟锑酸盐/分子式:C5H9F6N2Sb
  7. html特殊字符PPT,HTML 文档的结构,标签,在 HTML 文档中插入特殊字符,创建超级链接.ppt...
  8. 一下科技秒拍升级:打造更符合市场的短视频平台
  9. 现代家用计算机有哪些名称大全,当前个人计算机(电脑)有哪些形式
  10. 银行业务管理软件(7)