參考:Paint System。
Paint System 是使用相同 API 在屏幕或打印設(shè)備上繪畫的系統(tǒng)。這里的 API 主要基于 QPainter,QPaintDevice,和 QPaintEngine 類。
QPainter用于執(zhí)行繪制操作,QPaintDevice是可以使用QPainter繪制的二維空間的抽象,并且QPaintEngine提供了繪制器用來在不同類型的設(shè)備上繪制的接口。QPaintEngine類由QPainter和QPaintDevice內(nèi)部使用,并且除非它們創(chuàng)建自己的設(shè)備類型,否則對應用程序程序員而言是隱藏的。

更多細節(jié)參考 Topics:
1 繪圖設(shè)備
QPaintDevice類是可以用QPainter進行繪圖的設(shè)備對象(也可以視為畫板)的基類。paint device(QPaintDevice 及其子類的實例對象)是可以使用QPainter繪圖的二維空間的一種抽象。其默認坐標系的原點位于左上角。X 向右增加,Y 向下方增加。單位是一個像素。
QPaintDevice 被 QBitmap, QImage, QPagedPaintDevice, QPicture, QPixmap, QGLFramebufferObject, QGLPixelBuffer, QPrinter, QSvgGenerator 繼承。

QPaintDevice 的繪圖功能由 QWidget[1], QImage[2], QPixmap[3], QPicture[4], 和 QOpenGLPaintDevice[5]實現(xiàn)。QPaintDevice構(gòu)造一個繪畫設(shè)備且只能從QPaintDevice的子類調(diào)用此構(gòu)造函數(shù)。
對新后端的支持可以通過從QPaintDevice類派生并重新實現(xiàn) virtual 函數(shù) paintEngine() 來實現(xiàn),以告訴QPainter應該使用哪個繪畫引擎在此特定設(shè)備上進行繪制。為了真正能夠在設(shè)備上繪畫,此繪畫引擎必須是通過從QPaintEngine類派生而創(chuàng)建的自定義繪畫引擎。
警告:Qt 要求在創(chuàng)建任何繪畫設(shè)備之前必須存在 QGuiApplication對象。繪畫設(shè)備訪問窗口系統(tǒng)資源,并且在創(chuàng)建應用程序?qū)ο笾安粫跏蓟@些資源。
QPaintDevice類提供了幾個返回各種設(shè)備指標(metrics)的函數(shù):depth()函數(shù)返回其位深度(位平面的數(shù)量)。height()函數(shù)以默認坐標系單位(default coordinate system units,例如QPixmap和QWidget的像素)返回其高度,而heightMM()返回以毫米為單位的設(shè)備高度。類似地,width()和widthMM()函數(shù)分別以默認坐標系單位和毫米為單位返回設(shè)備的寬度?;蛘?,通過將所需的PaintDeviceMetric指定為參數(shù),可以使用 protected 函數(shù)metric()(PySide2.QtGui.QPaintDevice.PaintDeviceMetric)來檢索度量信息。
logicalDpiX() 和 logicalDpiY() 函數(shù)以每英寸點數(shù)返回設(shè)備的水平和垂直分辨率(resolution)。physicalDpiX() 和 physicalDpiY() 函數(shù)也返回以英寸為單位的設(shè)備分辨率,但請注意,如果邏輯分辨率和物理分辨率不同,則相應的QPaintEngine必須處理映射。最后,colorCount()函數(shù)返回可用于繪制設(shè)備的不同顏色的數(shù)量。
1.1 OffScreen Rendering
GPU 屏幕渲染有兩種方式:
- On-Screen Rendering 當前屏幕渲染,是指渲染操作是在當前用于顯示屏幕緩沖區(qū)中進行的。
- Off-Screen Rendering 離屏渲染,指的是GPU在當前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作。
另一種特殊的離屏渲染:CPU 渲染
如果我們重寫了 drawRect 方法,并且使用任何 Core Graphics 的技術(shù)進行了繪制操作,就涉及到了 CPU 渲染。整個渲染過程由 CPU 在 App 內(nèi)同步地完成,渲染得到的 bitmap 最后再交由 GPU 用于顯示。
相比于當前屏幕渲染,離屏渲染的代價比較高,主要體現(xiàn)在兩個方面:
- 創(chuàng)建新緩沖區(qū)
- 想要離屏渲染,就必須創(chuàng)建一個新的緩沖區(qū);
- 上下文切換
- 離屏渲染的整個過程,需要多次切換上下文環(huán)境:先是從當前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上。而上下文環(huán)境的切換是要付出很大代價的。
1.2 圖像處理類
Qt 提供了四個用于處理圖像數(shù)據(jù)的類:QImage,QPixmap,QBitmap和QPicture。
QImage是為 I/O 設(shè)計的,并針對直接像素訪問和操作進行了優(yōu)化,而QPixmap是為在屏幕上顯示圖像而設(shè)計和優(yōu)化的。QBitmap只是繼承QPixmap并確保深度為 1 的便捷類。如果QPixmap對象確實是位圖,則isQBitmap()函數(shù)將返回True,否則返回False。最后,QPicture類是一種繪畫設(shè)備,可以記錄并重放QPainter命令。
2 Drawing
QPainter提供了高度優(yōu)化的功能,可以完成大多數(shù) GUI 程序所需的功能。它可以繪制所有內(nèi)容,從簡單的圖形基元(由QPoint,QLine,QRect,QRegion 和 QPolygon 類表示)到復雜的形狀(如矢量路徑)。 在 Qt 中,矢量路徑由QPainterPath類表示。QPainterPath提供了一個用于繪畫操作的容器,從而可以構(gòu)造和重用圖形形狀。
painter path 是由直線和曲線組成的對象。例如,矩形由直線組成,橢圓由曲線組成。與常規(guī)繪圖操作相比,painter path 的主要優(yōu)勢在于,復雜的形狀僅需要創(chuàng)建一次;那么僅使用 drawPath() 函數(shù)即可將它們繪制多次。
QPainterPath 對象可用于填充,概述和裁剪。要為給定的 painter path 生成可填充的輪廓,請使用 QPainterPathStroker 類。
使用 QPen 類繪制線條和輪廓(Lines and outlines)。筆由其樣式(style,i.e. its line-type),寬度,畫刷(brush),如何繪制端點(endpoints,cap-style)以及如何繪制兩條相連線之間的連接(join-style)定義。筆刷是一個 QBrush 對象,用于填充用筆生成的筆劃,即QBrush類定義填充圖案。
QPainter還可以繪制對齊的文本和像素圖。
繪制文本時,使用 QFont 類指定字體。Qt 將使用具有指定屬性的字體,或者如果不存在匹配的字體,則 Qt 將使用最接近的匹配安裝字體。可以使用 QFontInfo 類來檢索實際使用的字體的屬性。此外,QFontMetrics 類提供字體度量,而 QFontDatabase 類提供有關(guān)基礎(chǔ)窗口系統(tǒng)中可用字體的信息。
通常,QPainter會繪制“自然”坐標系,但是它能夠使用 QTransform 類執(zhí)行視圖和世界變換。
繪制時,像素渲染由“抗鋸齒”渲染提示控制。RenderHint枚舉用于指定QPainter的標志,任何給定的引擎可能會或可能不會考慮這些標志。Antialiasing 值指示引擎應盡可能對圖元的邊緣進行抗鋸齒,即通過使用不同的顏色強度對邊緣進行平滑處理。
3 Filling
使用QBrush類填充形狀。畫刷由其顏色和樣式(即填充樣式)定義。
Qt 中的任何顏色都由支持 RGB,HSV 和 CMYK 顏色模型的 QColor 類表示。QColor還支持 alpha 混合的輪廓和填充(指定透明效果),并且該類與平臺和設(shè)備無關(guān)(使用 QColormap 類將顏色映射到硬件)。
可用的填充模式由BrushStyle枚舉描述。這些包括從均勻顏色到非常稀疏的圖案的基本圖案,各種線條組合,漸變填充和紋理。Qt 提供了 QGradient 類來定義自定義漸變填充,而紋理圖案是使用QPixmap類指定的。
QGradient類與QBrush結(jié)合使用以指定漸變填充。

Qt 當前支持三種類型的漸變填充:線性漸變(Linear gradients)在起點和終點之間插值顏色;徑向漸變(radial gradients)在焦點與終點之間的圓上的終點之間插值顏色;圓錐形漸變(conical gradients)在中心點周圍插值顏色。
4 Coordinate System
有關(guān)繪制系統(tǒng)使用的坐標系的信息。坐標系由QPainter類控制。QPainter與QPaintDevice和QPaintEngine類一起構(gòu)成了 Qt 繪畫系統(tǒng) Arthur 的基礎(chǔ)。
paint device 的默認坐標系的原點位于左上角。X 值向右增加,Y 值向下增加。在基于像素的設(shè)備上,默認單位是一個像素,在打印機上,默認單位是一個點(1/72 英寸)。
logical QPainter 坐標到 physical QPaintDevice 坐標的映射由 QPainter 的轉(zhuǎn)換矩陣,視口(viewport)和 “window”處理。默認情況下,邏輯坐標系和物理坐標系重合。QPainter還支持坐標轉(zhuǎn)換(例如旋轉(zhuǎn)和縮放)。
4.1 Rendering
4.2 Logical Representation
圖形基元的大?。▽挾群透叨龋┦冀K與其數(shù)學模型相對應,而忽略用以下方式呈現(xiàn)的筆的寬度:

4.3 Aliased Painting
繪制時,像素渲染由 Antialiasing 渲染提示控制。RenderHint 枚舉用于指定QPainter的標志,任何給定的引擎可能會或可能不會考慮這些標志。 Antialiasing 值指示引擎應盡可能對圖元的邊緣進行抗鋸齒,即通過使用不同的顏色強度對邊緣進行平滑處理。但是默認情況下,畫家是別名的,并且適用其他規(guī)則:當使用一個像素寬的筆進行渲染時,像素將在數(shù)學上定義的點的右側(cè)和下方渲染。 例如:

當使用像素數(shù)為偶數(shù)的筆進行渲染時,將在數(shù)學定義的點周圍對稱地渲染像素,而使用像素數(shù)為奇數(shù)的筆進行渲染時,備用像素將在數(shù)學點的右邊和下方渲染 與一像素的情況一樣。更多信息參考:Qt:QRect 和 QRectF。
請注意,由于歷史原因,right()和bottom()函數(shù)的返回值偏離矩形的真實右下角。QRect的right()函數(shù)返回left()+ width() -1,bottom()函數(shù)返回top()+ height() -1。我們建議您改用QRectF:QRectF類使用浮點坐標定義平面中的矩形以提高準確性(QRect使用整數(shù)坐標),而right()和bottom()函數(shù)確實返回真正的右下角?;蛘?,使用QRect,應用x()+ width()和y()+ height()來找到右下角,并避免使用right()和bottom()函數(shù)。
4.4 Anti-aliased Painting
如果您設(shè)置QPainter的抗鋸齒渲染提示(anti-aliasing render hint),則像素將在數(shù)學定義的點的兩側(cè)對稱地渲染:

4.5 Transformations
默認情況下,QPainter在關(guān)聯(lián)設(shè)備自己的坐標系上運行,但是它也完全支持仿射坐標變換。您可以使用 scale() 函數(shù)按給定的偏移量縮放坐標系,可以使用 rotate() 函數(shù)順時針旋轉(zhuǎn)坐標系,并可以使用 translate() 函數(shù)平移坐標系(即將給定偏移量添加到點)。

您還可以使用shear()函數(shù)圍繞原點扭曲坐標系。 所有轉(zhuǎn)換操作都在QPainter的轉(zhuǎn)換矩陣上進行,您可以使用worldTransform()函數(shù)進行檢索。矩陣變換將平面中的一個點轉(zhuǎn)換為另一個點。如果需要反復進行相同的轉(zhuǎn)換,則還可以使用QTransform對象以及worldTransform()和setWorldTransform()函數(shù)。您隨時可以調(diào)用save()函數(shù)保存QPainter的轉(zhuǎn)換矩陣,該函數(shù)將矩陣保存在內(nèi)部堆棧中。restore()函數(shù)將其彈出。經(jīng)常需要轉(zhuǎn)換矩陣,這是在各種繪圖設(shè)備上重復使用相同的繪圖代碼時。如果不進行轉(zhuǎn)換,則結(jié)果將緊密綁定到繪畫設(shè)備的分辨率。打印機具有高分辨率,例如 每英寸600點,而屏幕通常每英寸72至100點。
4.6 Window-Viewport Conversion
使用QPainter進行繪制時,我們使用邏輯坐標指定點,然后將其轉(zhuǎn)換為繪畫設(shè)備的物理坐標。
邏輯坐標到物理坐標的映射由QPainter的世界轉(zhuǎn)換worldTransform()以及viewport()和window()處理。視口表示指定任意矩形的物理坐標。“window” 在邏輯坐標中描述了相同的矩形。 默認情況下,邏輯坐標系和物理坐標系重合,并且等效于繪畫設(shè)備的矩形。使用窗口視口轉(zhuǎn)換,可以使邏輯坐標系適合您的首選項。該機制還可以用于使繪圖代碼獨立于 paint device。例如,您可以通過調(diào)用setWindow()函數(shù)使邏輯坐標從擴展到
,并在中心具有
:
painter = QPainter(self)
painter.setWindow(QRect(-50, -50, 100, 100))
現(xiàn)在,邏輯坐標對應于繪圖設(shè)備的物理坐標
。獨立于繪制設(shè)備,繪制代碼將始終在指定的邏輯坐標上運行。
通過設(shè)置“window”或視口矩形,可以對坐標進行線性變換。請注意,“window”的每個角都映射到視口的相應角,反之亦然。因此,通常最好使視口和“window”保持相同的寬高比以防止變形:
int side = qMin(width(), height())
int x = (width() - side / 2);
int y = (height() - side / 2);
painter.setViewport(x, y, side, side);
如果將邏輯坐標系設(shè)為正方形,則還應該使用setViewport()函數(shù)將視口設(shè)為正方形。在上面的示例中,我們將其等效為適合繪畫設(shè)備矩形的最大正方形。在設(shè)置窗口或視口時,通過考慮繪畫設(shè)備的尺寸,可以使繪圖代碼獨立于繪畫設(shè)備。請注意,窗口-視口轉(zhuǎn)換僅是線性轉(zhuǎn)換,即不執(zhí)行裁剪。這意味著,如果在當前設(shè)置的“window”之外進行繪制,則您的繪制仍將使用相同的線性代數(shù)方法轉(zhuǎn)換為視口。

視口,"window"和轉(zhuǎn)換矩陣確定邏輯QPainter坐標如何映射到繪圖設(shè)備的物理坐標。默認情況下,世界變換矩陣為單位矩陣,并且"window"和視口設(shè)置等同于繪畫設(shè)備的設(shè)置,即世界,"window"和設(shè)備坐標系是等效的,但是正如我們所看到的,系統(tǒng)可以 使用轉(zhuǎn)換操作和窗口視口轉(zhuǎn)換進行操縱。上圖說明了該過程。示例可參考:Analog Clock Example。
5 Reading and Writing Image Files
讀取圖像的最常見方式是通過QImage和QPixmap的構(gòu)造函數(shù),或者通過調(diào)用load()函數(shù)。另外,Qt 提供了QImageReader類,該類提供了對該過程的更多控制。根據(jù)圖像格式的基本支持,該類提供的功能可以節(jié)省內(nèi)存并加快圖像加載速度。
同樣,Qt 提供了QImageWriter類,該類支持在存儲圖像之前設(shè)置格式特定的選項,例如 gamma 級別,壓縮級別和質(zhì)量。如果不需要此類選項,則可以改用save()函數(shù)。
QMovie 是內(nèi)部使用QImageReader類顯示動畫的便捷類。創(chuàng)建后,QMovie類將提供用于運行和控制給定動畫的各種功能。
QImageReader和QImageWriter類依賴于QImageIOHandler類,該類是 Qt 中所有圖像格式的通用圖像 I/O 接口。QImageReader和QImageWriter在內(nèi)部使用QImageIOHandler對象向 Qt 添加對不同圖像格式的支持。
可通過supportedImageFormats()和supportedImageFormats()函數(shù)獲得受支持文件格式的列表。Qt 默認情況下支持多種文件格式,此外,可以將新格式添加為插件。QImageReader和QImageWriter類文檔中列出了當前支持的格式。
Qt 的插件機制還可以用于編寫自定義圖像格式處理程序。這是通過派生QImageIOHandler類并創(chuàng)建一個QImageIOPlugin對象來完成的,該對象是用于創(chuàng)建QImageIOHandler對象的工廠。安裝插件后,QImageReader和QImageWriter將自動加載插件并開始使用。
-
QWidget類是 Qt Widgets 模塊中用戶界面元素的基類。它從窗口系統(tǒng)接收鼠標,鍵盤和其他事件,并在屏幕上繪制其自身的表示。 ? -
QImage類提供了獨立于硬件的圖像表示,該圖像表示針對 I/O 以及直接像素訪問和操作進行了設(shè)計和優(yōu)化。QImage支持多種圖像格式,包括單色,8 位,32 位和 alpha 混合圖像。將QImage用作繪制設(shè)備的一個優(yōu)點是可以以與平臺無關(guān)的方式保證任何繪制操作的像素準確性。另一個好處是可以在當前 GUI 線程之外的其他線程中執(zhí)行繪制。 ? -
QPixmap類是一種屏幕外圖像表示形式,其設(shè)計和優(yōu)化用于在屏幕上顯示圖像。與QImage不同,像素圖中的像素數(shù)據(jù)是內(nèi)部的,并由基礎(chǔ)窗口系統(tǒng)管理,即像素只能通過QPainter函數(shù)或?qū)?code>QPixmap轉(zhuǎn)換為QImage來訪問。為了使用QPixmap優(yōu)化繪圖,Qt 提供了 QPixmapCache 類,該類可用于存儲臨時像素圖,這些像素圖的生成成本很高,并且不使用超出緩存限制的存儲空間。Qt 還提供了 QBitmap 便利類,繼承了QPixmap。QBitmap保證單色(1位深度)像素圖,主要用于創(chuàng)建自定義 QCursor 和 QBrush 對象,構(gòu)造 QRegion 對象。你可以使用QPixmap的isQBitmap()函數(shù)來確定這個QPixmap是不是一個QBitmap。 ? -
QPicture類是一種繪畫設(shè)備,可以記錄和重放QPainter命令。圖片以平臺無關(guān)的格式將Painter命令序列化到 IO 設(shè)備。QPicture也是與分辨率無關(guān)的,即QPicture可以在看起來相同的不同設(shè)備(例如 svg,pdf,ps,打印機和屏幕)上顯示。Qt 提供load()和save()函數(shù),以及用于加載和保存圖片的流運算符(streaming operators)。 ? -
OpenGL Paint Device,Qt 提供的類使在 Qt 應用程序中使用 OpenGL 變得容易。例如,
QOpenGLPaintDevice啟用 OpenGL API 以使用QPainter進行渲染。 ?