Qt Paint System 概述
- 一、概述
- 二、繪圖設備和后端
- 1. Widget
- 2. Image
- 3. Pixmap
- 4. OpenGL繪制設備
- 5. Picture
- 6. 自定義繪制后端
- 三、繪圖與填充
- 1. Drawing
- 2. 填充 Filling
- 四、坐標系統
- 1. 渲染
- Window-Viewport轉換
- 五、讀寫圖像文件
- 1. QMovie
- 六、繪圖相關設備
一、概述
Qt的paint系統可以使用相同的API在屏幕和打印設備上進行繪圖,它主要是基于QPainter、QPaintDevice和QPaintEnengine類。
QPainter用于執行繪制操作,QPaintDevice是一個二維空間的抽象,可以使用QPainter在其上進行繪制,QPaintEngine提供了 QPainter 用于在不同類型設備上繪制的界面。QPaintEngine 類由 QPainter 和 QPaintDevice 在內部被使用,并且對我們應用開發程序員隱藏,除非我們創建自己的繪圖設備類型。
這種方法的主要好處是,所有繪制都遵循相同的繪制通道,這使得添加新功能的支持變得容易,并為不支持的功能提供默認實現。換句話說就是擴展性很強的。
二、繪圖設備和后端
QPaintDevice 類是可繪制對象的基類,即 QPainter 可以在任何 QPaintDevice 的子類上繪制。QPaintDevice 的繪圖功能由 QWidget、QImage、QPixmap、QPicture、QPrinter 和 QOpenGLPaintDevice 實現。
下面提到的這些類都是繼承了 QPaintDevice 因此就具備了 繪制的功能
1. Widget
QWidget類是Qt Widgets模塊中用戶界面元素的基類。它從window系統接收鼠標、鍵盤和其他事件,并在屏幕上顯示自己。就是窗體系統中的重要一環
2. Image
QImage類提供了一個獨立于硬件的圖像表示,為I/O和直接像素訪問和操作進行了設計和優化。QImage支持多種圖像格式,包括單色、8位、32位和alpha混合圖像。
使用QImage作為繪制設備的兩個優點:
- 可以以一種平臺無關的方式保證任何繪制操作的像素準確性。
- 繪圖可以在當前GUI線程之外的另一個線程中執行。
3. Pixmap
QPixmap類是為在屏幕上顯示圖像而設計和優化的屏幕外圖像表示。與 QImage 不同,pixmap中的像素數據是內部的,由底層窗口系統管理,即像素只能通過 QPainter 函數或將 QPixmap 轉換為 QImage 來訪問。
為了優化使用QPixmap繪圖,Qt提供了QPixmapCache類,該類可用于存儲臨時的pixmap,這些臨時的pixmap生成成本很高,而不會使用超過緩存限制的存儲空間。
Qt還提供了QBitmap便利類,它繼承了QPixmap。QBitmap保證單色(1位深度)像素圖,主要用于創建自定義QCursor和QBrush對象,構造QRegion對象。
4. OpenGL繪制設備
如前所述,Qt提供了一些類,使得在Qt應用程序中使用OpenGL變得容易。例如,QOpenGLPaintDevice啟用了用于QPainter渲染的OpenGL API。
5. Picture
QPicture類是一個記錄和回放QPainter命令的繪制設備。圖片以獨立于平臺的格式將 painter 命令序列化到IO設備。QPicture也是分辨率獨立的,即一個 QPicture 可以在不同的設備(例如svg, pdf, ps,打印機和屏幕)上顯示,看起來相同。
Qt提供了 QPicture::load() 和 QPicture::save() 函數以及用于加載和保存圖片的流操作符。
6. 自定義繪制后端
對新的后端的支持可以通過從QPaintDevice類派生并重新實現虛擬的 QPaintDevice::paintEngine() 函數來告訴 QPainter 應該使用哪個繪制引擎在這個特定的設備上繪制。
要真正能夠在設備上繪制,這個繪制引擎必須是通過派生QPaintDevice 類創建的自定義繪制引擎。
三、繪圖與填充
1. Drawing
QPainter提供了高度優化的函數來完成大多數GUI程序所需的繪圖。它可以繪制任何東西,從簡單的圖形基元(由QPoint、QLine、QRect、QRegion和QPolygon類表示)到復雜的形狀,如矢量路徑。在Qt矢量路徑由QPainterPath類表示。QPainterPath為繪制操作提供了一個容器,使圖形形狀能夠被構建和重用。
1. QPainterPath
QPainterPath 是由直線和曲線組成的對象。例如,矩形由直線組成,橢圓由曲線組成。
與普通的繪制操作相比,painter paths的主要優點是復雜的形狀只需要創建一次;然后只需調用QPainter::drawPath()函數就可以多次繪制它們。
QPainterPath對象可用于填充、輪廓和裁剪。要為給定的painter路徑生成可填充的輪廓,使用QPainterPathStroker類。
線和輪廓使用QPen類繪制。一支筆是由它的樣式(即它的線條類型)、寬度、筆刷、端點的繪制方式(cap-style)以及兩條連接的線之間的連接方式(join-style)來定義的。鋼筆的筆刷是一個QBrush對象,用于填充鋼筆生成的筆觸,即QBrush類定義了填充模式。
QPainter還可以繪制對齊的文本和像素地圖。
繪制文本時,字體使用 QFont 類指定。Qt 將使用指定屬性的字體,如果不存在匹配的字體,Qt將使用安裝的最接近的字體。實際使用的字體屬性可以使用 QFontInfo 類取得。此外,QFontMetrics 類提供字體測量,QFontDatabase 類提供有關底層窗口系統中可用字體的信息。
通常,QPainter在一個“自然”坐標系中繪制,但它能夠使用 QTransform 類執行 視圖 和 世界 坐標系之間的轉換。有關更多信息,請參見坐標系統,它也描述了渲染過程,即邏輯表示和渲染像素之間的關系,以及反鋸齒繪制的好處。
2. 反鋸齒的繪圖
繪制時,像素渲染由 QPainter::Antialiasing 渲染提示控制。枚舉 QPainter::RenderHint 用于指定 QPainter 的標志,這些標志可能被任何給定的引擎使用,也可能不被任何給定的引擎使用。
QPainter::Antialiasing 值表示如果可能的話,引擎應該消除圖元的邊緣,即通過使用不同的顏色強度平滑邊緣。
2. 填充 Filling
使用QBrush類填充形狀。畫筆是由它的顏色和樣式(即它的填充模式)定義的。
Qt中的任何顏色都由QColor類表示,該類支持RGB、HSV和CMYK顏色模型。QColor還支持alpha混合輪廓和填充(指定透明效果),并且該類與平臺和設備無關(使用QColormap類將顏色映射到硬件)。
可用的填充圖案由Qt::BrushStyle枚舉描述。這些包括基本模式,從統一的顏色到非常稀疏的模式,各種線條組合,梯度填充和紋理。Qt提供了QGradient類來定義自定義的漸變填充,而使用QPixmap類來指定紋理模式。
1. QGradient
QGradient類與QBrush類結合使用來指定梯度填充。
Qt 目前支持三種類型的漸變填充:線性漸變在起點和終點之間插入顏色,徑向漸變在焦點和圓周上的終點之間插入顏色,圓錐漸變在中心點周圍插入顏色。
四、坐標系統
坐標系統由QPainter類控制。與QPaintDevice和QPaintEngine類一起,QPainter構成了Qt繪畫系統的基礎。QPainter用于執行繪制操作,QPaintDevice是一個二維空間的抽象,可以使用QPainter在其上進行繪制,QPaintEngine提供了 QPainter 用于在不同類型設備上繪制的界面。
QPaintDevice 類是可繪制對象的基類:它的繪制功能由QWidget、QImage、QPixmap、QPicture和QOpenGLPaintDevice類繼承。繪制設備的默認坐標系統起源于左上角。x值向右增大,y值向下增大。在基于像素的設備上,默認單位是一個像素,在打印機上是一個點(1/72英寸)。
邏輯QPainter坐標到物理QPaintDevice坐標的映射是由QPainter的轉換矩陣、視口和 “窗口” 處理的。邏輯坐標系統和物理坐標系統默認重合。QPainter還支持坐標變換(例如旋轉和縮放)。
1. 渲染
1. 邏輯表示
圖形基元的大小(寬度和高度)總是對應于它的數學模型,而忽略渲染時使用的筆的寬度:
2. 非反鋸齒繪畫
繪制時,像素渲染由QPainter::Antialiasing渲染參數控制。
RenderHint枚舉用于指定QPainter的標志,任何給定的引擎都可能使用這些標志,也可能不使用。QPainter::Antialiasing值表示如果可能的話,引擎應該消除圖元的邊緣,即通過使用不同的顏色強度平滑邊緣。
但默認情況下,painter是帶鋸齒渲染的的,并且適用其他規則:當使用單像素寬的筆渲染時,像素將被渲染到數學定義的點的右側和下方。例如:
當使用偶數像素的筆進行渲染時,像素將圍繞數學定義的點進行對稱渲染,而使用奇數像素的筆進行渲染時,空閑像素將被渲染到數學點的右側和下方,就像 1 像素的情況一樣。具體的例子請參見下面的QRectF圖。
請注意,由于歷史原因,QRect::right()和QRect::bottom()函數的返回值偏離了矩形的真正右下角。
QRect的right()函數返回left() + width() - 1,而 bottom()函數返回 top() + height() - 1。上圖中的綠色點顯示了這些函數的返回坐標。
我們建議你直接使用QRectF: QRectF類使用浮點坐標在平面中定義一個矩形來保證精度(QRect使用整數坐標),而QRectF::right()和QRectF::bottom()函數則返回真正的右下角。
或者,使用QRect,應用x() + width()和y() + height()來找到右下角,避免使用right()和bottom()函數。
3. 反鋸齒的繪畫
如果你設置了QPainter的反鋸齒渲染提示,像素將在數學定義點的兩側對稱渲染:
4. 轉換
默認情況下,QPainter在關聯設備自己的坐標系統上操作,但它也完全支持仿射坐標變換。
我們可以使用QPainter::scale()函數按給定的偏移量縮放坐標系統,我們可以使用QPainter::rotate()函數順時針旋轉它,并且可以使用QPainter::translate()函數平移它(即向點添加給定的偏移量)。
你也可以使用QPainter::shear()函數圍繞原點旋轉坐標系統。所有的轉換操作都是在QPainter的轉換矩陣上進行的,你可以使用QPainter::worldTransform()函數來獲取這個矩陣。矩陣將平面上的一個點變換為另一個點。
如果你需要反復使用相同的變換,你也可以使用 QTransform 對象以及 QPainter::worldTransform() 和 QPainter::setWorldTransform() 函數。你可以在任何時候通過調用 QPainter::save() 函數來保存 QPainter 的轉換矩陣,該函數將矩陣保存在內部堆棧上。QPainter::restore() 函數彈出它。
在各種繪圖設備上重用相同的繪圖代碼時,經常需要轉換矩陣。如果沒有變換,結果將與繪制設備的分辨率緊密綁定。打印機的分辨率很高,例如每英寸600點,而屏幕的分辨率通常在每英寸72到100點之間。
Analog Clock示例展示了如何使用QPainter的轉換矩陣繪制自定義控件的內容。
我們建議在進一步閱讀之前編譯并運行此示例。特別是,嘗試將窗口大小調整為不同的大小。
void AnalogClockWindow::render(QPainter *p){static const QPoint hourHand[3] = {QPoint(7, 8),QPoint(-7, 8),QPoint(0, -40)};static const QPoint minuteHand[3] = {QPoint(7, 8),QPoint(-7, 8),QPoint(0, -70)};QColor hourColor(127, 0, 127);QColor minuteColor(0, 127, 127, 191);p->setRenderHint(QPainter::Antialiasing);p->translate(width() / 2, height() / 2);int side = qMin(width(), height());p->scale(side / 200.0, side / 200.0);
我們轉換坐標系統,使點(0,0)位于控件的中心,而不是位于左上角。我們還按邊/ 100縮放系統,邊是控件的寬度或高度,以最短的為準。我們希望時鐘是方的,即使設備不是方的。
這將給我們一個200 × 200的正方形區域,原點(0,0)在中心,我們可以在上面繪圖。我們繪制的內容將顯示在適合控件的最大可能的正方形中。
另請參見窗口-視口轉換部分。
QTime time = QTime::currentTime();p->save();p->rotate(30.0 * ((time.hour() + time.minute() / 60.0)));p->drawConvexPolygon(hourHand, 3);p->restore();
我們通過旋轉坐標系并調用QPainter::drawConvexPolygon()來繪制時鐘的時針。由于旋轉,它被畫在了正確的方向上。
多邊形被指定為一個交替的x, y值數組,存儲在hourHand靜態變量中(在函數開始處定義),它對應于四個點(2,0),(0,2),(- 2,0)和(0,-25)。
對代碼周圍的 QPainter::save() 和 QPainter::restore() 的調用保證了后面的代碼不會受到我們使用的轉換的干擾。
p->save();p->rotate(6.0 * (time.minute() + time.second() / 60.0));p->drawConvexPolygon(minuteHand, 3);p->restore();
我們對時鐘的分針也是這樣做的,分針由四個點 (1,0)、(0,1)、(- 1,0) 和 (0,-40) 定義。這些坐標指定了一個比分針更細更長的指針。
p->setPen(minuteColor);for (int j = 0; j < 60; ++j) {if ((j % 5) != 0)p->drawLine(92, 0, 96, 0);p->rotate(6.0);}
最后,我們繪制時鐘面,它由12條30度間隔的短線組成。
Window-Viewport轉換
當使用QPainter繪圖時,我們使用邏輯坐標指定點,然后將其轉換為繪制設備的物理坐標。
邏輯坐標到物理坐標的映射是由QPainter的世界變換 worldTransform() (在變換部分描述)和 QPainter 的 viewport() 和 window() 處理的。視口表示指定任意矩形的物理坐標。“窗口”在邏輯坐標中描述相同的矩形。默認情況下,邏輯和物理坐標系統是重合的,相當于繪制設備的矩形。
使用窗口-視口轉換,我們可以使邏輯坐標系統符合我們的偏好。所述還可用于使所述繪圖代碼獨立于所述QPaintDevice 。例如,你可以通過調用QPainter::setwinwindow()函數,使邏輯坐標從(-50,-50)擴展到(50,50),中間是(0,0):
QPainter painter(this);painter.setWindow(QRect(-50, -50, 100, 100));
現在,邏輯坐標(-50,-50)對應于繪制設備的物理坐標(0,0)。獨立于繪制設備,我們的繪制代碼將始終對指定的邏輯坐標進行操作。相當于我們可以手動的設置這種坐標關系。自己定義這種坐標的方便關系。
通過設置“窗口”或視口矩形,可以執行坐標的線性變換。請注意,“窗口”的每個角落都映射到viewport的相應角落,反之亦然。出于這個原因,讓視口和“窗口”保持相同的寬高比以防止變形通常是一個好主意:
int side = qMin(width(), height())int x = (width() - side / 2);int y = (height() - side / 2);painter.setViewport(x, y, side, side);
如果我們將邏輯坐標系統設置為正方形,我們也應該使用QPainter::setViewport()函數將視口設置為正方形。在上面的例子中,我們將其設置為適合繪制設備矩形的最大正方形。通過在設置窗口或視口時考慮繪制設備的大小,可以保持繪制代碼獨立于繪制設備。
請注意,窗口-視口轉換只是一個線性轉換,即它不執行裁剪。這意味著如果你在當前設置的“窗口”之外繪制,你的繪畫仍然會使用相同的線性代數方法轉換到視口。
視口、“窗口” 和 變換矩陣 決定了邏輯QPainter坐標如何映射到繪制設備的物理坐標。默認情況下,世界變換矩陣是單位矩陣,“窗口”和視口設置等同于繪制設備的設置,即世界、“窗口”和設備坐標系是等效的,但正如我們所看到的,系統可以使用轉換操作和窗口-視口轉換來操縱。
五、讀寫圖像文件
讀取圖像最常見的方法是通過QImage和QPixmap的構造函數,或者調用QImage::load()和QPixmap::load()函數。此外,Qt提供了QImageReader類,它提供了對進程的更多控制。根據圖像格式的底層支持,類提供的函數可以節省內存并加快圖像加載速度。
同樣,Qt提供了QImageWriter類,它支持在存儲圖像之前設置特定格式的選項,如伽馬值、壓縮級別和質量。如果我們不需要這些選項,我們可以使用QImage::save()或QPixmap::save()代替。
1. QMovie
QMovie是一個用于顯示動畫的方便類,在內部使用QImageReader類。創建后,QMovie類為運行和控制給定動畫提供了各種函數。
QImageReader和QImageWriter類依賴于QImageIOHandler類,QImageIOHandler類是Qt中所有圖像格式的通用圖像I/O接口,QImageReader和QImageWriter內部使用QImageIOHandler對象來為Qt添加對不同圖像格式的支持。
QImageReader::supportedImageFormats()和QImageWriter::supportedImageFormats()函數提供了支持的文件格式列表。Qt默認支持幾種文件格式,此外還可以通過插件添加新格式。目前支持的格式在QImageReader和QImageWriter類文檔中列出。
Qt的插件機制也可以用來編寫自定義圖像格式處理程序。這是通過從QImageIOHandler類派生,并創建一個QImageIOPlugin對象來完成的,該對象是創建QImageIOHandler對象的工廠。當插件安裝后,QImageReader和QImageWriter會自動加載插件并開始使用它。
六、繪圖相關設備
Paint 相關 | 功能 |
---|---|
QBitmap | 單色(1位深度)像素圖 |
QBrush | 定義由QPainter繪制的形狀的填充模式 |
QColor | 基于RGB、HSV或CMYK值的顏色 |
QColorSpace | 色彩空間抽象 |
QColorTransform | 顏色空間之間的轉換 |
QColormap | 將獨立于設備的QColors映射到依賴于設備的像素值 |
QConicalGradient | 與QBrush組合使用指定一個錐形梯度刷 |
QFont | 指定用于繪制文本的字體查詢 |
QFontMetrics | 字體度量信息 |
QFontMetricsF | 字體度量信息 |
QGenericMatrix | 表示N列M行NxM變換矩陣的模板類 |
QGradient | 與QBrush組合使用來指定梯度填充 |
QIcon | 可伸縮的圖標在不同的模式和狀態 |
QIconEngine | QIcon渲染器的抽象基類 |
QImage | 獨立于硬件的圖像表示,允許直接訪問像素數據,并可以用作繪制設備 |
QImageReader | 格式獨立接口,用于從文件或其他設備讀取圖像 |
QImageWriter | 格式獨立接口,用于將圖像寫入文件或其他設備 |
QLine | 使用整數精度的二維向量 |
QLineF | 二維向量使用浮點精度 |
QLinearGradient | 與QBrush組合使用來指定一個線性梯度刷 |
QMargins | 定義矩形的四個邊距 |
QMarginsF | 定義矩形的四個邊距 |
QPagedPaintDevice | 表示支持多個頁面的繪制設備 |
QPaintDevice | 可以用QPainter繪制的對象的基類 |
QPaintEngine | QPainter如何在給定平臺上繪制到給定設備的抽象定義 |
QPainter | 在部件和其他繪制設備上執行低級繪制 |
QPainterPath | 用于繪制操作的容器,使圖形能夠被構建和重用 |
QPainterPathStroker | 用于為給定的繪制路徑生成可填充的輪廓 |
QPdfWriter | 類以生成可用作繪圖設備的pdf |
QPen | 定義一個QPainter應該如何繪制線條和形狀的輪廓 |
QPixmap | 可以用作繪畫設備的屏幕外圖像表示 |
QPoint | 使用整數精度定義平面中的一個點 |
QPointF | 在平面中使用浮點精度定義一個點 |
QPolygon | 使用整數精度的點向量 |
QPolygonF | 使用浮點精度的點向量 |
QRadialGradient | 使用組合與QBrush指定一個徑向梯度刷 |
QRect | 使用整數精度在平面內定義一個矩形 |
QRectF | 在平面中使用浮點精度定義一個矩形 |
QRegion | 指定繪制器的剪輯區域 |
QRgba64 | Struct包含64位RGB顏色 |
QSize | 使用整數點精度定義二維對象的大小 |
QSizeF | 使用浮點精度定義二維對象的大小 |
QStylePainter | 用于在窗口組件中繪制QStyle元素的便利類 |
QSupportedWritingSystems | 在使用內部Qt fontdatabase注冊字體時使用 |
QSvgGenerator | 用于創建SVG繪圖的繪圖設備 |
QSvgRenderer | 用于將SVG文件的內容繪制到繪圖設備上 |
QSvgWidget | 用于顯示可伸縮矢量圖形(SVG)文件內容的控件 |
QTransform | 指定坐標系統的2D變換 |
QVector2D | 表示二維空間中的向量或頂點 |