日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

小谈一下Qt的绘制引擎(结尾有彩蛋)

發(fā)布時(shí)間:2023/12/16 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 小谈一下Qt的绘制引擎(结尾有彩蛋) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

公眾號:張小飛那些事兒

小談一下Qt的繪制引擎(結(jié)尾有彩蛋)

這一篇算是我給部門分享的一篇業(yè)務(wù)基礎(chǔ)吧。以及說一下自己對Qt繪制引擎的理解以及及時(shí)的復(fù)盤。

先談一個(gè)疑問?如何設(shè)計(jì)一個(gè)優(yōu)秀的繪制引擎。

注意下這里,我說的是繪制引擎,而不是光柵化引擎。這有本質(zhì)的區(qū)別。

繪制引擎是我們開發(fā)者用的一些常見的接口。光柵化引擎我認(rèn)為是繪制引擎一部分的實(shí)現(xiàn),所以這里只講外層的東西。逃)

個(gè)人認(rèn)為,Qt是把C++ OOP的特性用到滾瓜爛熟的框架-封裝,繼承,多態(tài)。

廢話不多說,先舉個(gè)栗子吧。

舉個(gè)🌰

假如要畫一條線,需要哪幾步

要把畫一條線總共需要幾個(gè)角色(要把大象裝冰箱總共分幾步)

第一步,需要一個(gè)人(畫線的方法)。(廢話)

第二步,需要一個(gè)筆。

第三步,需要一張紙。

換成Qt來畫線的話那就是

第一步,需要一個(gè)光柵化引擎(QPaintEngine)

第二步,需要一個(gè)筆(QPainter)

第三步,需要一個(gè)設(shè)備(QPaintDevice)

所以Qt給我們暴露的接口就是這三個(gè)

  • QPaintEngine

  • QPainter

  • QPaintDevice

Qt的繪制引擎簡介

Qt官方簡介

QPaintEngine,QPainter,QPaintDevice組成了Qt繪制界面的基礎(chǔ)。

直接貼上三個(gè)類的官方說明介紹

順便打個(gè)廣告,如果有興趣參與Qt文檔的翻譯,歡迎參與項(xiàng)目 QtDocumentCN/QtDocumentCN: Qt中文文檔翻譯 (github.com)

一下說明來自項(xiàng)目QtDocumentCN

QPaintEngine

QPaintEngine類為QPainter提供了如何在指定繪圖設(shè)備上(譯者注:一般為QPaintDevice的派生)繪制的一些抽象的方法。

Qt為不同的painter后端提供了一些預(yù)設(shè)實(shí)現(xiàn)的QPaintEngine

譯者注:提供一個(gè)更加好理解的說法。QPainter的Qt實(shí)現(xiàn)一般默認(rèn)調(diào)用的是QPaintEngine的方法。

現(xiàn)在QPaintEngine主要提供的是Qt自帶的光柵化引擎(raster engine),Qt在他所有支持的平臺上,提供了一個(gè)功能完備的光柵化引擎。

在Windows, X11 和 macOS平臺上,Qt自帶的光柵化引擎都是QWidget這個(gè)基礎(chǔ)類的默認(rèn)的繪制方法的提供者,亦或是QImage的繪制方法的提供者。當(dāng)然有一些特殊的繪制設(shè)備的繪制引擎不提供對應(yīng)的繪制方法,這時(shí)候就會調(diào)用默認(rèn)的光柵化引擎。

當(dāng)然,我們也為OpenGL(可通過QOpenGLWidget訪問)跟打印(允許QPainter在QPrinter對象上繪制,用于生成pdf之類的)也提供了對應(yīng)的QPaintEngine的實(shí)現(xiàn)。

譯者注: QPainter,QPainterEngine,QPaintDevice三個(gè)是相輔相成的。

  • QPainter為開發(fā)者提供外部接口方法用于繪制
  • QPaintEngine為QPainter提供一些繪制的具體實(shí)現(xiàn)
  • QPaintDevice為QPainter提供一個(gè)繪圖設(shè)備,用于顯示亦或儲存。

如果你想使用QPainter繪制自定義的后端(譯者注:這里可以理解為QPaintDevice)。你可以繼承QPaintEngine,并實(shí)現(xiàn)其所有的虛函數(shù)。然后子類化QPaintDevice并且實(shí)現(xiàn)它的純虛成員函數(shù)(QPaintDevice::paintEngine())。

由QPaintDevice創(chuàng)建QPaintEngine,并維護(hù)其生命周期。

另請參見QPainter,QPaintDevice::paintEngine()和Paint System

QPaintDevice

翻譯TODO

QPainter

翻譯TODO

舉個(gè)Qt里實(shí)現(xiàn)QPaintEngine相關(guān)的例子

首先在Qt的源碼里打開終端執(zhí)行命令

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在各個(gè)不同端的派生,有興趣可以搜下qpaintdevice相關(guān)的,也差不多都是這樣。

比如qpaintengine_raster.cpp 就是Qt自己的光柵化引擎實(shí)現(xiàn),qpaintengine_x11.cpp就是在Linux下默認(rèn)跟x11交互的光柵化實(shí)現(xiàn)。。。

當(dāng)然并不是所有的派生都會有自己獨(dú)立的cpp文件,或者叫相關(guān)的cpp 。可以對比Qt的官方API來對照下

枚舉類型枚舉值描述
ConstantValueDescription
QPaintEngine::X110
QPaintEngine::Windows1
QPaintEngine::MacPrinter4
QPaintEngine::CoreGraphics3macOS的Quartz2D(CoreGraphics)
QPaintEngine::QuickDraw2macOS的QuickDraw
QPaintEngine::QWindowSystem5嵌入式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的引擎

說下Qt繪制一條線的流程

現(xiàn)在有這樣的代碼,我們來在Qt中繪制一條線

QLineF line(10.0, 80.0, 90.0, 20.0);QPainter painter(this); painter.drawLine(line);

如果我們的Qt把渲染引擎設(shè)置成了raster引擎,那么qpainter的實(shí)現(xiàn)本質(zhì)上是調(diào)用的QPaintEngine的相關(guān)代碼。

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); //這里調(diào)用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); //這里會走模擬繪制本質(zhì)上也會走一個(gè)engine}return;}d->engine->drawLines(lines, lineCount); //或者這里調(diào)用qpaintengine }

那么就調(diào)用到了QPaintEngineRaster的相關(guān)實(shí)現(xiàn)。

QRasterPaintEngine繼承自QPaintEngineEx,QPaintEngineEx繼承自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在畫畫的時(shí)候本質(zhì)上是QPaintEngine提供的方法。

關(guān)于QPaintDevice

由QPaintDevice創(chuàng)建QPaintEngine,并維護(hù)其生命周期。by官方文檔

上面的代碼中,是這樣初始化QPainter的。

我們一般重寫一個(gè)QWidget的paintevent的時(shí)候才會這樣。

//這里的this實(shí)際上就是一個(gè)QWidget,QWidget繼承自QPaintDevice QPainter painter(this);

QWidget繼承自QPaintDevice,看下源碼實(shí)現(xiàn)

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

QPaintDevice本質(zhì)上就是一個(gè)繪制設(shè)備,供我們使用,由于QPaintDevice創(chuàng)建QPaintEngine ,所以QPaintDevice跟QPaintEngine一樣,也會有很多種類型的派生。

  • QGLFramebufferObject
  • QGLPixelBuffer
  • QImage,
  • QOpenGLPaintDevice,
  • QPagedPaintDevice
  • QPaintDeviceWindow,
  • QPicture
  • QPixmap,
  • QSvgGenerator
  • QWidget

憑記憶說一下繪制的一些流程

QPainter在繪制的時(shí)候是有很多講究的,就拿標(biāo)臟來說吧。

假如有這么一段代碼

QPen pen;QPainter painter(this); painter.setPen(pen);

這時(shí)候setPen的時(shí)候,QPainter里的QPaintEngine會直接設(shè)置這個(gè)flag

QPaintEngine::DirtyPen,

然后內(nèi)部再走標(biāo)臟之后需要走的邏輯,QPaintEngine使用函數(shù)QPaintEngine::updateState()來通知QPainter的延遲刷新。所以基本上,Qt的繪制效率跟效果都是有保證的。

如果你想繼承QPaintEngine來實(shí)現(xiàn)自己的光柵化引擎的話,不一定是光柵化。比如像Qt那樣支持保存成pdf也可以。

必須更新下面所有的標(biāo)臟狀態(tài)(譯者注:比如你自定義一個(gè)QPaintEngine,就需要處理上面的所有的狀態(tài)的刷新)

比如你setFont,那么狀態(tài)就QPaintEngine::DirtyFont

setBrush,那么狀態(tài)就QPaintEngine::DirtyBrush

枚舉類型枚舉值描述
QPaintEngine::DirtyPen0x0001畫筆已經(jīng)標(biāo)臟,應(yīng)刷新
QPaintEngine::DirtyBrush0x0002畫刷已經(jīng)標(biāo)臟,應(yīng)刷新
QPaintEngine::DirtyBrushOrigin0x0004畫刷原始數(shù)據(jù)已經(jīng)變化,應(yīng)刷新
QPaintEngine::DirtyFont0x0008字體發(fā)生變化,應(yīng)刷新
QPaintEngine::DirtyBackground0x0010背景標(biāo)臟,應(yīng)刷新
QPaintEngine::DirtyBackgroundMode0x0020背景狀態(tài)標(biāo)臟,應(yīng)刷新
QPaintEngine::DirtyTransform0x0040當(dāng)前矩陣標(biāo)臟,應(yīng)刷新
QPaintEngine::DirtyClipRegion0x0080當(dāng)前裁剪區(qū)域標(biāo)臟,應(yīng)刷新
QPaintEngine::DirtyClipPath0x0100裁剪路徑標(biāo)臟,應(yīng)刷新
QPaintEngine::DirtyHints0x0200當(dāng)前繪制精度標(biāo)志變化,應(yīng)刷新
QPaintEngine::DirtyCompositionMode0x0400繪制組合模式變化,應(yīng)刷新
QPaintEngine::DirtyClipEnabled0x0800無論是否當(dāng)前可裁剪,都應(yīng)刷新
QPaintEngine::DirtyOpacity0x1000當(dāng)前透明度已經(jīng)更改,應(yīng)當(dāng)使用QPaintEngine::updateState()來進(jìn)行刷新
QPaintEngine::AllDirty0xffff內(nèi)部枚舉使用變量。

擴(kuò)展用法

WPS針對Qt的繪制引擎做了很多延伸操作。

舉個(gè)很簡單的栗子

比如有一個(gè)圖片類型,Qt是不支持的,那么我該如何接入。

首先我要繼承QPaintDevice來叫一個(gè)QXXXXImage。(類似QImage那樣,當(dāng)然你也可以直接繪制到QWidget上)

也要繼承一個(gè)QPaintEngine來叫一個(gè)QXXXXXEngine。(類似QPaintEngine::SVG那樣)

QPainter不變。

在繪制圖片的時(shí)候,QPainter會直接調(diào)用到自己實(shí)現(xiàn)的QXXXXXEngine,這里接到設(shè)備上,來自己實(shí)現(xiàn)繪制這個(gè)新格式的流程。

這樣,我們就什么都不用變,就可以支持一種新格式了,你QPainter原來該怎么drawImage,還怎么draw。

Qt源碼里這里當(dāng)然有一個(gè)參考的栗子

可以參考源碼

./5.15.2/Src/qtbase/src/printsupport/kernel/qprintengine_pdf.cpp

Qt的PDF引擎很好的實(shí)現(xiàn)了自己的QPaintEngine,跟QPaintDevice,要不然怎么能夠支持導(dǎo)出pdf呢!

結(jié)尾彩蛋

WPS當(dāng)然遵守Qt的LGPL協(xié)議,使用的Qt版本已經(jīng)在GitHub開源了。

開源鏈接。 https://github.com/kingsoft-wps/qt5

公眾號:張小飛那些事兒

總結(jié)

以上是生活随笔為你收集整理的小谈一下Qt的绘制引擎(结尾有彩蛋)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。