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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

公眾號:張小飛那些事兒

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

這一篇算是我給部門分享的一篇業務基礎吧。以及說一下自己對Qt繪制引擎的理解以及及時的復盤。

先談一個疑問?如何設計一個優秀的繪制引擎。

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

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

個人認為,Qt是把C++ OOP的特性用到滾瓜爛熟的框架-封裝,繼承,多態。

廢話不多說,先舉個栗子吧。

舉個🌰

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

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

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

第二步,需要一個筆。

第三步,需要一張紙。

換成Qt來畫線的話那就是

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

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

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

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

  • QPaintEngine

  • QPainter

  • QPaintDevice

Qt的繪制引擎簡介

Qt官方簡介

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

直接貼上三個類的官方說明介紹

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

一下說明來自項目QtDocumentCN

QPaintEngine

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

Qt為不同的painter后端提供了一些預設實現的QPaintEngine

譯者注:提供一個更加好理解的說法。QPainter的Qt實現一般默認調用的是QPaintEngine的方法。

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

在Windows, X11 和 macOS平臺上,Qt自帶的光柵化引擎都是QWidget這個基礎類的默認的繪制方法的提供者,亦或是QImage的繪制方法的提供者。當然有一些特殊的繪制設備的繪制引擎不提供對應的繪制方法,這時候就會調用默認的光柵化引擎。

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

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

  • QPainter為開發者提供外部接口方法用于繪制
  • QPaintEngine為QPainter提供一些繪制的具體實現
  • QPaintDevice為QPainter提供一個繪圖設備,用于顯示亦或儲存。

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

由QPaintDevice創建QPaintEngine,并維護其生命周期。

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

QPaintDevice

翻譯TODO

QPainter

翻譯TODO

舉個Qt里實現QPaintEngine相關的例子

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

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::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繪制一條線的流程

現在有這樣的代碼,我們來在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繼承自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在畫畫的時候本質上是QPaintEngine提供的方法。

關于QPaintDevice

由QPaintDevice創建QPaintEngine,并維護其生命周期。by官方文檔

上面的代碼中,是這樣初始化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

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

QPainter在繪制的時候是有很多講究的,就拿標臟來說吧。

假如有這么一段代碼

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

這時候setPen的時候,QPainter里的QPaintEngine會直接設置這個flag

QPaintEngine::DirtyPen,

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

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

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

比如你setFont,那么狀態就QPaintEngine::DirtyFont

setBrush,那么狀態就QPaintEngine::DirtyBrush

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

擴展用法

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

舉個很簡單的栗子

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

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

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

QPainter不變。

在繪制圖片的時候,QPainter會直接調用到自己實現的QXXXXXEngine,這里接到設備上,來自己實現繪制這個新格式的流程。

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

Qt源碼里這里當然有一個參考的栗子

可以參考源碼

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

Qt的PDF引擎很好的實現了自己的QPaintEngine,跟QPaintDevice,要不然怎么能夠支持導出pdf呢!

結尾彩蛋

WPS當然遵守Qt的LGPL協議,使用的Qt版本已經在GitHub開源了。

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

公眾號:張小飛那些事兒

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。