關於GDI+中GraphicsPath進行合并(Union)截切(Exclude)等編程的探討(1)

來源:互聯網
上載者:User

我們知道,在GDI+中,兩個圖形路徑(GraphicsPath)的地區(Region)合并,我們可以採用Region.Union方法進行。但使用它之後,我們再想取得合并後的Region的GraphicsPath卻變得不再可能。比如1紅色部分:

圖1  合并GraphicsPath後想要達到的效果

它由兩個橢圓共同組成:

            Rectangle rect1 = new Rectangle(100, 100, 178, 178);
            Rectangle rect2 = new Rectangle(230, 100, 178, 178);
            GraphicsPath gp = new GraphicsPath(FillMode.Winding);
            gp.AddEllipse(rect1);
            gp.AddEllipse(rect2);

            Bitmap bmp = new Bitmap(520, 520);
            Graphics g = Graphics.FromImage(bmp);
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            g.SmoothingMode = SmoothingMode.HighQuality;

            g.DrawPath(new Pen(Color.Red, 3f), gp);
            picEncode.Image = bmp;

但上面的代碼並無法得到圖1的結果,而是2所示的樣式:

圖2  使用graphicsPath.Add()方法產生的兩條圖形路徑的輪廊繪製

有沒有辦法將這兩個GraphicsPath真正合并呢?

首先來看看GraphicsPath背後的東東:gp.PathData,它裡麵包含有路徑的點 (points)  和點的類型(types)數組。

對於每一點,byte值0 到 2 都表示點的類型,而 3 到 7 則儲存一組可指定點的屬性(Attribute) 的標誌。其中值的含義如下:
0表示此點為圖形的起始點;1表示此點為直線兩個端點之其中一點;3表示此點為三次貝茲路徑的端點或控制點;0x7為遮罩所有位元,但表示點類型的三個低階位不在此列;0x20指定此點為標記;0x80表示此點為封閉式子路徑的最後一個點。

這是其原始的定義:
typedef enum  {
  PathPointTypeStart          = 0,
  PathPointTypeLine           = 1,
  PathPointTypeBezier         = 3,
  PathPointTypePathTypeMask   = 0x7,
  PathPointTypePathDashMode   = 0x10,
  PathPointTypePathMarker     = 0x20,
  PathPointTypeCloseSubpath   = 0x80,
  PathPointTypeBezier3        = 3 
} PathPointType;
我們來看看圖2的GraphicsPath(通過C#寫的小工具得到其背後發生的秘密,哈哈~~):

GraphicsPath path = new GraphicsPath(
new PointF[] {
new PointF(278F,99F),new PointF(278F,148.1533F),new PointF(238.1533F,188F),new PointF(189F,188F),new PointF(139.8466F,188F),
new PointF(100F,148.1533F),new PointF(100F,99F),new PointF(100F,49.84666F),new PointF(139.8466F,10F),new PointF(189F,10F),
new PointF(238.1533F,10F),new PointF(278F,49.84666F),new PointF(278F,99F),new PointF(408F,99F),new PointF(408F,148.1533F),
new PointF(368.1533F,188F),new PointF(319F,188F),new PointF(269.8466F,188F),new PointF(230F,148.1533F),new PointF(230F,99F),
new PointF(230F,49.84666F),new PointF(269.8466F,10F),new PointF(319F,10F),new PointF(368.1533F,10F),new PointF(408F,49.84666F),
new PointF(408F,99F)},
            new System.Byte[] { 0,3,3,3,3,3,3,3,3,3,3,3,131,0,3,3,3,3,3,3,3,3,3,3,3,131 });

注意上面最後一行那個Byte數組,裡面有0,3,131等值,其中131是128(即0x80)+3的組合,說明該點為封閉子路徑的最後一點及是貝茲路徑的端點。

3為關鍵點顯示出來的樣示(其中藍色為關鍵點):

圖3  圖形路徑的關鍵點

通過上面可以看出:左右兩個圓的交叉點位置並不是關鍵點!

麻煩就出來了,我們必須找到這些交叉點,然後,將交叉點也作為關鍵點放入最後的GraphicsPath中,這樣才能解決圖形路徑的合并問題。

那麼,如何找到交叉點呢?對於矩形(如4)、圓形等較規則的圖形路徑而言還比較好辦,我們可以想辦法通過數學公式來進行。

圖4  兩個矩形構建的GraphicsPath(其中藍色點為關鍵點)

圖4的源碼僅是將圖2所示的代碼中加粗的兩行代碼中的AddEllipse改為AddRectangle而已。很明顯,求它們的交叉點是非常容易的。但是,對於不規則的貝茲路徑等則變得非常棘手了。

大家如果有興趣,可以先看看這篇文章,比較基礎,但非常有用,不過是英文的,如果你的英文不咋的,請耐著性子看下去,看明白了,一定會有收穫的!

http://processingjs.nihongoresources.com/bezierinfo/

順便還可以看看這個:

http://processingjs.nihongoresources.com/bezierinfo/sketchsource.php?sketch=cubicSubdivision

http://fei.edu.br/~psergio/CG_arquivos/IntroSplines.pdf

http://www.uio.no/studier/emner/matnat/ifi/INF3320/h03/undervisningsmateriale/lecture8.pdf

http://losingfight.com/blog/2011/07/08/how-to-implement-boolean-operations-on-bezier-paths-part-2/

http://www.cs.berkeley.edu/~hling/research/paper/surface.htm

http://blog.sina.com.cn/s/blog_640531380100q669.html

(未完待續,準備另起一篇專門介紹這個問題)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.