用Direct2D繪製SVG (1)

矢量圖形繪製, 首先想到使用SVG作爲圖像格式,起步需要的就是SVG文件格式。

GitHub找了一圈,nanosvg是一個很好的C庫。
nanosvg是一個很符合KISS的庫,分爲parser 和 Rasterizer,
Parser部分,都在 #include "nanosvg.h" 頭文件中,記得要#define NANOSVG_IMPLEMENTATION
Rasterizer部分,都在 #include "nanosvgrast.h" 頭文件中,同樣要定義 #define NANOSVGRAST_IMPLEMENTATION

如果需求是 SVG to PNG,這2者用上,加個PNG的write庫(這裏面是stb_image_write.h)即可。
然而,如果每次繪製都需要把矢量搞成點陣圖,非常消耗資源。

另外: nanosvg只支持SVG v1.1,各種Filter就別想了。因此最大的用處在於參考價值。
對照着http://www.w3school.com.cn/svg/
Parser部分, SVG既然是矢量繪製,按圖形學的常識,基本圖形就是line和 cubic bezier。
再預定義的形狀元素,可被開發者使用和操作:
矩形 <rect>
圓形 <circle>
橢圓 <ellipse>
線 <line>
折線 <polyline>
多邊形 <polygon>
路徑 <path>

Path特別強大,直接以命令方式幾乎可以組成任何圖形。
M = moveto
L = lineto
H = horizontal lineto
V = vertical lineto
C = curveto
S = smooth curveto
Q = quadratic Belzier curve
T = smooth quadratic Belzier curveto
A = elliptical Arc
Z = closepath
看Parser代碼,nanosvg非常簡單粗暴,所有圖形一律規整爲cubic bezier。

Trace出Path代碼,嘗試使用D2d繪製這些CubicBezier。
按MSDN說明,先從ID2D1Factory, CreatePathGeometry出一個ID2D1PathGeometry
再通過這個ID2D1PathGeometry, Open一個ID2D1GeometrySink
注意這個ID2D1GeometrySink,包含實際功能,可以直接使用。
而對應的ID2D1SimplifiedGeometrySink,則不包含實際功能,必須繼承實現接口作爲sink回調。

  CComPtr<ID2D1PathGeometry> path;
  _factory->CreatePathGeometry(&path);

  {
    CComPtr<ID2D1GeometrySink> sink;
    path->Open(&sink);

    float af[] = {4.00,52,
      4.00,38, 4.00,31, 6.36,26, 9.29,19, 14.72,13, 21.54,10, 27.03, 8, 
      34.02, 8, 48.00, 8, 61.98, 8, 68.97, 8, 74.46,10, 81.28,13, 86.71,19, 89.64,26, 92.00,31, 
      92.00,38, 92.00,52, 92.00,66, 92.00,73, 89.64,78, 86.71,85, 81.28,91, 74.46,94, 68.97,96, 
      61.98,96, 48.00,96, 34.02,96, 27.03,96, 21.54,94, 14.72,91, 9.29,85, 6.36,78, 4.00,73, 
      4.00,66, 4.00,52, 4.00,52, 4.00,52, 4.00,52};

    sink->BeginFigure(
      D2D1::Point2F(af[0],af[1]),
      D2D1_FIGURE_BEGIN_FILLED
      );

    for( int i = 0; i < 12; i++ ) {
      sink->AddBezier( BezierSegment( Point2F(af[i*6+2],af[i*6+3]),
                                      Point2F(af[i*6+4],af[i*6+5]), 
                                      Point2F(af[i*6+6],af[i*6+7])));
    }

    sink->EndFigure(D2D1_FIGURE_END_OPEN);
    sink->Close();

須注意, BezierSegment是一個 (controlpoint1, controlpoint2, endpoint) 的序列。
其中上一個endpoint(或者moveTo Point) 作爲startpoint,也算是cubic bezier在各種圖形庫的一個使用常態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章