C++ 使用 Skia 绘图(已停更)
注意:因篇幅問(wèn)題,該文已停更,作者將重新劃分章節(jié)以便持續(xù)更新!本文將會(huì)被刪除!
索引
Skia API C++ 文檔(非官方)
-
前言
-
Skia 準(zhǔn)備
-
項(xiàng)目實(shí)踐——Hello World(使用Skia繪制并輸出為PNG圖片)
-
Skia 繪制流程(對(duì)Skia的繪制流程進(jìn)行闡述)
-
項(xiàng)目實(shí)踐——繪制圖形(使用Skia繪制平面圖形并輸出為PNG圖片)
前言
筆者曾經(jīng)編譯過(guò)一款使用了Skia的軟件,于是查詢了一些資料,了解到Skia是一個(gè)2D向量圖形處理函數(shù)庫(kù)。只是可惜,筆者嘗試用它寫(xiě)程序,但是官方文檔國(guó)內(nèi)無(wú)法訪問(wèn),網(wǎng)上資料極少,并且歷史悠久,不適合新版。于是筆者在自己查閱大量文章慢慢摸索,一點(diǎn)一點(diǎn)地完成這篇文章,為他人提供資料
Skia 準(zhǔn)備
下載Skia,國(guó)內(nèi)skia編譯門檻搞,所以此處直接使用Jetbrain在GitHub上的庫(kù)(本文使用m93)
Releases · JetBrains/skia-build (github.com)
分別下載Debug和Release兩個(gè)版本,放至一個(gè)目錄下(建議目錄)
Skia | Debug | | Debug壓縮包解壓 | Release | | Release壓縮包解壓項(xiàng)目實(shí)踐——【Hello World】
創(chuàng)建項(xiàng)目并設(shè)置項(xiàng)目屬性
創(chuàng)建控制臺(tái)項(xiàng)目,建議使用預(yù)編譯頭。此處開(kāi)始筆者將指引您如何配置項(xiàng)目屬性。首先,右鍵單擊項(xiàng)目→屬性(Properties)→C/C++→常規(guī)(General)→附加包含目錄(Additional Include Directories),按照上一步建議的目錄,分別對(duì)應(yīng)Debug和Release填入。
注意:Debug和Release一定要區(qū)分
鏈接靜態(tài)庫(kù)
因?yàn)槭浅鯇W(xué),暫時(shí)只用得到skia.lib
同樣的,右鍵單擊項(xiàng)目→屬性(Properties)→鏈接器(Linker)→常規(guī)(General)→附加庫(kù)目錄(Additional Library Directories),將Debug和Release中靜態(tài)庫(kù)的目錄分別對(duì)應(yīng)輸入。
執(zhí)行完上述步驟,再轉(zhuǎn)到鏈接器(Linker)→輸入(Input)→附加依賴項(xiàng)(Additional Dependencies)
引入頭文件
我們已經(jīng)配置好了Debug和Release環(huán)境,但是實(shí)際還需要一些東西。
在預(yù)編譯頭末尾添加以下代碼:
// Skia Dependencies #include <d3d12.h> #pragma comment(lib, "D3D12.lib") #include <d3dcompiler.h> #pragma comment(lib, "d3dcompiler.lib") #pragma comment(lib, "Opengl32.lib") #ifdef _DEBUG #include <dxgi1_3.h> #pragma comment(lib, "DXGI.lib") #endif // _DEBUG解釋一下,這些東西是Skia的依賴項(xiàng),所以一定要加上,因?yàn)橛杏玫紻XGIGetDebugInterface1(具體是什么我也不知道)但這是DXGI.lib的一個(gè)函數(shù),并且Debug環(huán)境下不可缺少,所以用預(yù)編譯命令設(shè)置在Debug模式下鏈接DXGI.lib。這樣就可以通過(guò)編譯。
注意:如果要引用Skia的頭文件,須在上述代碼片段前引入;如果預(yù)編譯頭文件中有Windows.h,請(qǐng)將其移至上述代碼后,否則會(huì)出現(xiàn)大量錯(cuò)誤
編寫(xiě)代碼
預(yù)編譯頭(筆者是pch.h文件)
// pch.h: This is a precompiled header file. // Files listed below are compiled only once, improving build performance for future builds. // This also affects IntelliSense performance, including code completion and many code browsing features. // However, files listed here are ALL re-compiled if any one of them is updated between builds. // Do not add files here that you will be updating frequently as this negates the performance advantage.#ifndef PCH_H #define PCH_H// add headers that you want to pre-compile here#endif //PCH_H// Skia #include <include/core/SkCanvas.h> #include <include/core/SkPaint.h> #include <include/core/SkPath.h> #include <include/core/SkShader.h> #include <src/utils/SkShaderUtils.h> #include <include/core/SkTypeface.h> #include <include/core/SkTypes.h> #include <include/core/SkFont.h> #include <include/core/SkFontStyle.h> #include <include/core/SkFontTypes.h> #include <include/core/SkSurface.h> #include <include/codec/SkCodec.h> #include <include/core/SkPngChunkReader.h> #include <src/core/SkShaderCodeDictionary.h> #include <src/core/SkBitmapDevice.h>// Skia Depency #include <d3d12.h> #pragma comment(lib, "D3D12.lib") #include <d3dcompiler.h> #pragma comment(lib, "d3dcompiler.lib") //#include <wingdi.h> #pragma comment(lib, "Opengl32.lib") #ifdef _DEBUG #include <dxgi1_3.h> #pragma comment(lib, "DXGI.lib") #endif // _DEBUG#include <Windows.h>主程序代碼(main.cpp)
#include "pch.h" #include <iostream>int main() {SkBitmap bitmap; //創(chuàng)建一個(gè)位圖設(shè)備SkImageInfo imageInfo = SkImageInfo::Make(480, 320, kBGRA_8888_SkColorType, kPremul_SkAlphaType); //設(shè)置位圖信息bitmap.allocPixels(imageInfo, imageInfo.minRowBytes()); //為位圖設(shè)備綁定信息和分配內(nèi)存SkCanvas canvas(bitmap); //創(chuàng)建畫(huà)布SkPaint paint; //創(chuàng)建畫(huà)筆canvas.clear(0x00000000); //將畫(huà)布清空并填充一種顏色,此處為黑色透明paint.setColor(SK_ColorBLACK); //設(shè)置畫(huà)筆顏色SkFont font; //創(chuàng)建字體設(shè)備font.setSize(64); //設(shè)置字體尺寸font.setTypeface(SkTypeface::MakeFromName("Microsoft YaHei", SkFontStyle::Normal())); //設(shè)置字體canvas.drawSimpleText("Hello World", sizeof("Hello World") - 1, SkTextEncoding::kUTF8, 0, 64, font, paint); //在畫(huà)布上繪制字體SkFILEWStream stream("D:\\test.png"); //創(chuàng)建文件輸出流SkEncodeImage(&stream, bitmap, SkEncodedImageFormat::kPNG, 100); //將位圖數(shù)據(jù)按照指定格式編碼并輸出到文件中return 0; }編譯運(yùn)行,此時(shí),代碼中對(duì)應(yīng)目錄下會(huì)出現(xiàn)一張圖片,上面有Hello World。至此,我們已經(jīng)完成了Skia的Hello World項(xiàng)目。
思路很簡(jiǎn)單,首先創(chuàng)建一個(gè)SkBitmap位圖設(shè)備,再創(chuàng)建一個(gè)SkImageInfo并填充圖片信息,然后與位圖設(shè)備綁定并為其分配內(nèi)存。
接著創(chuàng)建一個(gè)與之關(guān)聯(lián)的畫(huà)布SkCanvas,并對(duì)畫(huà)布進(jìn)行初始化(即清空畫(huà)布并填充顏色,注意這并非是必須的步驟,若忽略此步,將默認(rèn)為透明背景)。
創(chuàng)建一個(gè)SkPaint畫(huà)筆并設(shè)置顏色,再創(chuàng)建一個(gè)SkRect矩陣填充信息,畫(huà)筆用于控制繪制文本的顏色,矩陣用于控制文本所在的空間位置。
接著創(chuàng)建一個(gè)SkFont字體對(duì)象,設(shè)置字體與樣式,最后調(diào)用SkCanvas類中的drawSimpleText方法完成繪制。
最后用SkEncodeImage函數(shù)按照PNG格式對(duì)位圖設(shè)備中的圖像數(shù)據(jù)編碼并輸出到SkFILEWStream文件輸出流中。
代碼后的有關(guān)注釋如有錯(cuò)誤,請(qǐng)一定提出,筆者一定嚴(yán)加改正,萬(wàn)分感激!
注意!該代碼筆者測(cè)試時(shí)中文字符將不會(huì)顯示或顯示為方框,原因未知,測(cè)試時(shí)請(qǐng)盡量使用英文字符!如有朋友了解其原因,可向筆者發(fā)送私信,亦可評(píng)論區(qū)告知,筆者必將認(rèn)真閱讀!Skia作為一個(gè)工具,國(guó)內(nèi)有關(guān)其的文檔屈指可數(shù),大部分為老舊版本,難以與現(xiàn)今易得版本適配,為了補(bǔ)充這方面的空白,筆者誠(chéng)心希望廣大朋友共同努力!!!
另外,關(guān)于代碼中所用的類與方法等信息,為方便大家學(xué)習(xí),筆者打算另開(kāi)一條博文專門介紹。
API 文檔
Skia 繪制流程
本節(jié)我們將一起理清上述代碼的繪制思路。
在繪制前,我們有一下幾個(gè)問(wèn)題:
圍繞這三個(gè)問(wèn)題,我們逐一解決。
首先是在哪里進(jìn)行繪制?我們知道,要想進(jìn)行繪制,就需要一個(gè)載體來(lái)存儲(chǔ)圖像數(shù)據(jù)。因此,我們需要一塊內(nèi)存,而為了方便對(duì)這塊內(nèi)存的管理,Skia為我們準(zhǔn)備好了這樣一個(gè)設(shè)備——SkBitmap。這樣一個(gè)類,完美解決了我們的需求。接下來(lái)便是進(jìn)行繪制。
那如何進(jìn)行繪制?我們可以類比畫(huà)油畫(huà),SkBitmap相當(dāng)于配好了畫(huà)板。但我們并不知道我們需要畫(huà)多大,多寬,這是我們就需要為畫(huà)板固定好一張白紙用于承載我們的筆跡,這就相當(dāng)于讓SkBitmap分配一塊指定大小的內(nèi)存。同時(shí),我們不光還要知道內(nèi)存有多大,還需要知道用的是什么樣的顏色空間等等。于是,我們選擇使用Skia為我們準(zhǔn)備好的另一個(gè)設(shè)備——SkImageInfo,這個(gè)設(shè)備包含了關(guān)于“白紙”的長(zhǎng)寬,顏色標(biāo)準(zhǔn)等。接下來(lái)就是讓SkBitmap按照這些信息分配內(nèi)存。
再接下來(lái)
項(xiàng)目實(shí)踐——繪制圖形
繪制完歷史性的“Hello World”,想必讀者已經(jīng)迫不及待地渴望用Skia繪制圖形了。別急,本節(jié)我們將介紹使用Skia繪制基本圖形。
代碼如下,預(yù)編譯頭等不用修改,再次只貼出main()的代碼:
int main() {SkBitmap bitmap;SkImageInfo imageInfo = SkImageInfo::Make(500, 400, kBGRA_8888_SkColorType, kPremul_SkAlphaType);bitmap.allocPixels(imageInfo, imageInfo.minRowBytes());SkCanvas canvas(bitmap);SkPaint paint;canvas.clear(0xFFFFFFFF);paint.setColor(SK_ColorRED);SkPoint point; //創(chuàng)建一個(gè)點(diǎn),并設(shè)置坐標(biāo)point.fX = 50;point.fY = 50;canvas.drawCircle(point, (SkScalar)50, paint); //以上方創(chuàng)建的點(diǎn)為圓心繪制圓半徑為50pxpaint.setColor(SK_ColorBLUE);SkRect rect; //創(chuàng)建矩陣信息rect.fLeft = 100;rect.fRight = 250;rect.fTop = 0;rect.fBottom = 100;canvas.drawRect(rect, paint);paint.setColor(SK_ColorCYAN); //設(shè)置畫(huà)筆顏色rect.fLeft = 250; //重新填充矩陣信息rect.fRight = 500;rect.fTop = 0;rect.fBottom = 100;canvas.drawOval(rect, paint); //以新的矩陣信息,在其中繪制橢圓paint.setColor(SK_ColorGRAY); //設(shè)置畫(huà)筆顏色rect.fLeft = 0;rect.fRight = 250;rect.fTop = 100;rect.fBottom = 200;canvas.drawRoundRect(rect, (SkScalar)10, (SkScalar)10, paint); //以新的矩陣信息,按照10px的半長(zhǎng)軸和10px的半短軸繪制圓角矩形(因?yàn)榘腴L(zhǎng)軸半短軸相等所描述的是一個(gè)圓,但其本質(zhì)描述的是橢圓,因此會(huì)有兩個(gè)參數(shù)用于確定圓角程度)SkFILEWStream stream("D:\\test.png");SkEncodeImage(&stream, bitmap, SkEncodedImageFormat::kPNG, 100);return 0; }思路和上一個(gè)項(xiàng)目很像,便不過(guò)多贅述。這里總結(jié)一下SkCanvas中的幾個(gè)繪制圖形的方法:
drawCircle:繪制圓
drawRect:繪制矩形
drawOval:繪制橢圓
drawRoundRect:繪制圓角矩形
項(xiàng)目實(shí)踐——繪制中文字符
想必各位讀者在嘗試《項(xiàng)目實(shí)踐——【Hello World】》的時(shí)候,一定視圖講"Hello World"替換為其他字符,但是或許有的讀者視圖利用Skia繪制出中文字符,而最后的結(jié)果可能是中文字符變成方框、中文字符變成別的亂七八糟的字符或者含有中文字符而導(dǎo)致整個(gè)字符串繪制不出來(lái)。這些問(wèn)題筆者都經(jīng)常遇到過(guò)。所以本節(jié)我們將重點(diǎn)介紹利用Skia繪制中文字符。
【停更】
總結(jié)
以上是生活随笔為你收集整理的C++ 使用 Skia 绘图(已停更)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: i5-10400+ 华硕(ASUS)TU
- 下一篇: checksum algorithm a