关于裁剪区域SetClip的初步理解

先看一个关于setclip用法的初步使用的例子:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Drawing;
 6 using System.Windows.Forms;
 7 using System.Drawing.Drawing2D;
 8 
 9 namespace SetClipDemo
10 {
11     class SetClipDemo:Form
12     {
13         static void Main(string[] args)
14         {
15             Application.Run(new SetClipDemo());
16         }
17         public SetClipDemo()
18         {
19             Text = "Set Clip Demo";
20             ResizeRedraw = true;
21         }
22 
23         protected override void OnPaint(PaintEventArgs e)
24         {
25             Graphics grfx = e.Graphics;
26             GraphicsPath path = new GraphicsPath();
27             path.AddEllipse(0,0,ClientSize.Width/3 * 2,ClientSize.Height);
28             path.AddEllipse(ClientSize.Width / 3, 0, ClientSize.Width / 3 * 2, ClientSize.Height);
29             //grfx.DrawPath(new Pen(Color.Red), path);
30 
31             grfx.SetClip(path);
32             
33             grfx.FillPath(Brushes.Violet, path);
34         }
35     }
36 }

这个示例比较简单,就是画了两个椭圆,两个椭圆有交叉部分;而后,使用setclip进行区域裁剪,结果如下:

 

关于setclip用法的理解,书中有比较形象的解释,特引用如下

 

裁剪区域setclip生效以后,后续代码中的所涉及的内容,只能在所裁剪的区域中才能展现出来,如上述代码中的violet颜色,我们使用的代码是填充整个客户区的,但是裁剪区域之后,只能在裁剪区域中才可以看到violet颜色。

实际上,裁剪区域的本质是使用GraphicsPath创建的轮廓,经SetClip之后,在后台利用这个轮廓创建了一个Region区域对象,从而完成裁剪区域的工作的。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

下面再看了稍微复杂点的,依然以上面的示例为基础模型

 1 //---------------------------------------------------
 2 // ClippingCombinations.cs ?2001 by Charles Petzold
 3 //---------------------------------------------------
 4 using System;
 5 using System.Drawing;
 6 using System.Drawing.Drawing2D;
 7 using System.Windows.Forms;
 8 using PrintableForm_chen;
 9 
10 
11 class ClippingCombinations: PrintableForm
12 {
13      string   strCaption = "CombineMode = ";
14      MenuItem miCombineMode;
15 
16      public new static void Main()
17      {
18           Application.Run(new ClippingCombinations());
19      }
20      public ClippingCombinations()
21      {
22           Text = strCaption + (CombineMode)0;
23 
24           Menu = new MainMenu();
25           Menu.MenuItems.Add("&CombineMode");
26 
27           EventHandler ehClick = new EventHandler(MenuCombineModeOnClick);
28           
29           for (int i = 0; i < 6; i++)
30           {
31                MenuItem mi   = new MenuItem("&" + (CombineMode)i);
32                mi.Click     += ehClick;
33                mi.RadioCheck = true;
34 
35                Menu.MenuItems[0].MenuItems.Add(mi);
36           }
37           miCombineMode = Menu.MenuItems[0].MenuItems[0];
38           miCombineMode.Checked = true;
39      }
40      void MenuCombineModeOnClick(object obj, EventArgs ea)
41      {
42           miCombineMode.Checked = false;
43           miCombineMode = (MenuItem) obj;
44           miCombineMode.Checked = true;
45 
46           Text = strCaption + (CombineMode)miCombineMode.Index;
47           Invalidate();
48      }
49      protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
50      {
51           GraphicsPath path = new GraphicsPath();
52           path.AddEllipse(0, 0, 2 * cx / 3, cy);
53           grfx.SetClip(path);
54 
55           //path.Reset();
56           path.AddEllipse(cx / 3, 0, 2 * cx / 3, cy);
57           grfx.SetClip(path, (CombineMode)miCombineMode.Index);
58 
59           grfx.FillRectangle(Brushes.Red, 0, 0, cx, cy);
60      }
61 }

注意:第55行,将path.Reset();进行了注销;

下面是上述代码产生的结果,如下:

开始对为什么产生图片的结果进行分析

仅仅使用第一个setclip,产生如下效果

 

 而两个椭圆叠加使用setclip(类似本篇第一个示例那样)之后,产生的效果如下

 

 也就是说,在使用grfx.SetClip(path, (CombineMode)miCombineMode.Index);进行运算时,其它就是第一个裁剪区域和第二个裁剪区域的叠加运算

 

 

 

两个裁剪区域的叠加运算的计算规则如下:

明白了上述原理后,计算结果就非常明显了;再展示一下上述代码的计算结果

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

上面是注释掉path.Reset();的情况,再看一下启用path.Reset();的情况

 1 //---------------------------------------------------
 2 // ClippingCombinations.cs ?2001 by Charles Petzold
 3 //---------------------------------------------------
 4 using System;
 5 using System.Drawing;
 6 using System.Drawing.Drawing2D;
 7 using System.Windows.Forms;
 8 using PrintableForm_chen;
 9 
10 
11 class ClippingCombinations: PrintableForm
12 {
13      string   strCaption = "CombineMode = ";
14      MenuItem miCombineMode;
15 
16      public new static void Main()
17      {
18           Application.Run(new ClippingCombinations());
19      }
20      public ClippingCombinations()
21      {
22           Text = strCaption + (CombineMode)0;
23 
24           Menu = new MainMenu();
25           Menu.MenuItems.Add("&CombineMode");
26 
27           EventHandler ehClick = new EventHandler(MenuCombineModeOnClick);
28           
29           for (int i = 0; i < 6; i++)
30           {
31                MenuItem mi   = new MenuItem("&" + (CombineMode)i);
32                mi.Click     += ehClick;
33                mi.RadioCheck = true;
34 
35                Menu.MenuItems[0].MenuItems.Add(mi);
36           }
37           miCombineMode = Menu.MenuItems[0].MenuItems[0];
38           miCombineMode.Checked = true;
39      }
40      void MenuCombineModeOnClick(object obj, EventArgs ea)
41      {
42           miCombineMode.Checked = false;
43           miCombineMode = (MenuItem) obj;
44           miCombineMode.Checked = true;
45 
46           Text = strCaption + (CombineMode)miCombineMode.Index;
47           Invalidate();
48      }
49      protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
50      {
51           GraphicsPath path = new GraphicsPath();
52           path.AddEllipse(0, 0, 2 * cx / 3, cy);
53           grfx.SetClip(path);
54 
55           path.Reset();
56           path.AddEllipse(cx / 3, 0, 2 * cx / 3, cy);
57           grfx.SetClip(path, (CombineMode)miCombineMode.Index);
58 
59           grfx.FillRectangle(Brushes.Red, 0, 0, cx, cy);
60      }
61 }

 

有与无path.Reset();产生的结果还是不一样的

我们先看一下path.Reset();在CSDN中的定义:

----------------------------------------------------------------------------------------------------------------

GraphicsPath.Reset 方法

清空 PathPointsPathTypes 数组并将 FillMode 设置为 Alternate

C#
public void Reset ();
---------------------------------------------------------------------------------------------

为此,我添加了大量的验证代码进行验证,验证代码如下:
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Drawing;
  6 using System.Windows.Forms;
  7 using System.Drawing.Drawing2D;
  8 using PrintableForm_chen;
  9 
 10 namespace ClippingCombinations
 11 {
 12     class ClippingCombinations:PrintableForm
 13     {
 14         string strCaption = "CombineMode =";
 15         MenuItem miCombineMode;
 16 
 17         static void Main(string[] args)
 18         {
 19             Application.Run(new ClippingCombinations());
 20         }
 21         public ClippingCombinations()
 22         {
 23             Text = strCaption + (CombineMode)0;
 24 
 25             Menu = new MainMenu();
 26             Menu.MenuItems.Add("&CombineMode");
 27 
 28             EventHandler ehClick = new EventHandler(MenuCombineModeOnClick);
 29 
 30             for (int i = 0; i < 6; i++)
 31             {
 32                 MenuItem mi = new MenuItem("&" + (CombineMode)i);
 33                 mi.Click += ehClick;
 34                 mi.RadioCheck = true;
 35                 Menu.MenuItems[0].MenuItems.Add(mi);
 36             }
 37 
 38             miCombineMode = Menu.MenuItems[0].MenuItems[0];
 39             miCombineMode.Checked = true;
 40         }
 41 
 42         void MenuCombineModeOnClick(object obj, EventArgs e)
 43         {
 44             miCombineMode.Checked = false;
 45             miCombineMode = (MenuItem)obj;
 46             miCombineMode.Checked = true;
 47 
 48             Text = strCaption + (CombineMode)miCombineMode.Index;
 49             Invalidate();
 50         }
 51 
 52         protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
 53         {
 54             GraphicsPath path = new GraphicsPath();
 55             path.AddEllipse(0,0,2 *cx/3,cy);
 56             //测试使用
 57             grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Red, 0,0);
 58             grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Red, 0, 50);
 59             grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Red, 0, 100);
 60             grfx.DrawLine(new Pen(Color.Red, 5), 0, 0, cx, cy);
 61             //继续测试路径的数组坐标
 62             Console.WriteLine("这是第一个椭圆的路径点信息总量:{0}",path.PathPoints.Length);
 63             Console.WriteLine(path.PointCount);
 64             for (int i = 0; i < path.PathPoints.Length; i++)
 65             {
 66                 Console.WriteLine(path.PathPoints[i]);
 67 
 68                 //grfx.DrawLine(new Pen(Color.Green), cx / 2, cy / 2, path.PathPoints[i].X, path.PathPoints[i].Y);
 69             }
 70             //测试:第一个椭圆,描绘路径
 71             float width1 = 10;
 72             //grfx.DrawPath(new Pen(Brushes.Red,width1),path);
 73 
 74             //测试:第一个椭圆,填充路径
 75             //grfx.FillPath(Brushes.Black,path);
 76 
 77             grfx.SetClip(path);
 78             //测试:第一个椭圆裁剪之后,填充背景为粉色    
 79             grfx.FillRectangle(Brushes.Pink, 0, 0, cx, cy);
 80 
 81             //测试:第一个椭圆裁剪之后,描绘路径
 82             //grfx.DrawPath(new Pen(Brushes.Green, width1), path);
 83 
 84             //测试:第一个椭圆裁剪之后,填充路径
 85             //grfx.FillPath(Brushes.Black, path);
 86 
 87             //PathData pathdata = new PathData();
 88             //pathdata = path.PathData;
 89             //Console.WriteLine("使用PathData类型,{0}", pathdata.Points[0].X);
 90                         
 91             path.Reset();
 92 
 93             //PathData pathdata = new PathData();
 94             //pathdata = path.PathData;
 95             //Console.WriteLine("使用PathData类型,{0}", pathdata.Points[0].X);
 96 
 97             path.AddEllipse(cx/3,0,2 * cx/3,cy);
 98             //测试:添加第二个椭圆之后,描绘路径
 99             //grfx.DrawPath(new Pen(Brushes.Cyan, width1), path);
100 
101             //测试:添加第二个椭圆之后,填充路径
102             //grfx.FillPath(Brushes.Black, path);
103 
104             
105 
106             //测试使用
107             grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Purple, 0, 25);
108             grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Purple, 0, 75);
109             grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Purple, 0, 125);
110             grfx.DrawLine(new Pen(Color.Purple, 5), cx,0, 0, cy);
111             
112             //测试路径的数组并输出数组
113             Console.WriteLine(path.PathPoints.Length);
114             Console.WriteLine(path.PointCount);
115             for (int i = 0; i < path.PathPoints.Length; i++)
116             {
117                 Console.WriteLine(path.PathPoints[i]);
118                                 
119                 grfx.DrawLine(new Pen(Color.Green),cx/2,cy/2,path.PathPoints[i].X,path.PathPoints[i].Y);
120             }
121             grfx.DrawLines(new Pen(Color.Green),path.PathPoints);
122             //至此,以上均为测试内容
123 
124             //grfx.SetClip(path);
125 
126             //grfx.DrawPath(new Pen(Color.Violet), path);
127 
128             grfx.SetClip(path,(CombineMode)miCombineMode.Index);
129             //测试:第二次裁剪之后,路径数组的数量
130             Console.WriteLine("第二次裁剪之后,路径数组的数量是:{0}",path.PathPoints.Length);
131 
132             //测试:第二次裁剪之后,描绘路径
133             //grfx.DrawPath(new Pen(Brushes.Blue, width1), path);  //注:此行代码中没有发现路径
134 
135             //测试:第二次裁剪之后,填充路径
136             //grfx.FillPath(Brushes.Black, path);
137 
138             grfx.FillRectangle(Brushes.Yellow,0,0,cx,cy);
139             
140             grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!",Font,Brushes.Blue,0,150);
141             grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Blue, 0, cy-15);
142         }
143     }
144 }

在上述代码的验证中,在使用了path.Reset();之后,包括path.pathdata,path.PathPoints.Length等等都没有任何数据,确实已经清空了;

启用第93-95行的代码时,程序会报错,就是因为 path.Reset();之后,第一个椭圆的路径数据已经被清空。

但是,第二个椭圆的相关路径数据还存在的,这就造成第一个裁剪区域是左边的椭圆,第二个裁剪区域是右边的椭圆,两个裁剪区域如下:

整理出第一裁剪区域和第二裁剪区域,再进行grfx.SetClip(path,(CombineMode)miCombineMode.Index);运算就没有问题了!

再次展示一下效果

 

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using PrintableForm_chen;

namespace ClippingCombinations
{
    class ClippingCombinations:PrintableForm
    {
        string strCaption = "CombineMode =";
        MenuItem miCombineMode;

        static void Main(string[] args)
        {
            Application.Run(new ClippingCombinations());
        }
        public ClippingCombinations()
        {
            Text = strCaption + (CombineMode)0;

            Menu = new MainMenu();
            Menu.MenuItems.Add("&CombineMode");

            EventHandler ehClick = new EventHandler(MenuCombineModeOnClick);

            for (int i = 0; i < 6; i++)
            {
                MenuItem mi = new MenuItem("&" + (CombineMode)i);
                mi.Click += ehClick;
                mi.RadioCheck = true;
                Menu.MenuItems[0].MenuItems.Add(mi);
            }

            miCombineMode = Menu.MenuItems[0].MenuItems[0];
            miCombineMode.Checked = true;
        }

        void MenuCombineModeOnClick(object obj, EventArgs e)
        {
            miCombineMode.Checked = false;
            miCombineMode = (MenuItem)obj;
            miCombineMode.Checked = true;

            Text = strCaption + (CombineMode)miCombineMode.Index;
            Invalidate();
        }

        protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)
        {
            GraphicsPath path = new GraphicsPath();
            path.AddEllipse(0,0,2 *cx/3,cy);
            //测试使用
            grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Red, 0,0);
            grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Red, 0, 50);
            grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Red, 0, 100);
            grfx.DrawLine(new Pen(Color.Red, 5), 0, 0, cx, cy);
            //继续测试路径的数组坐标
            Console.WriteLine("这是第一个椭圆的路径点信息总量:{0}",path.PathPoints.Length);
            Console.WriteLine(path.PointCount);
            for (int i = 0; i < path.PathPoints.Length; i++)
            {
                Console.WriteLine(path.PathPoints[i]);

                //grfx.DrawLine(new Pen(Color.Green), cx / 2, cy / 2, path.PathPoints[i].X, path.PathPoints[i].Y);
            }
            //测试:第一个椭圆,描绘路径
            float width1 = 10;
            //grfx.DrawPath(new Pen(Brushes.Red,width1),path);

            //测试:第一个椭圆,填充路径
            //grfx.FillPath(Brushes.Black,path);

            grfx.SetClip(path);
            //测试:第一个椭圆裁剪之后,填充背景为粉色    
            grfx.FillRectangle(Brushes.Pink, 0, 0, cx, cy);

            //测试:第一个椭圆裁剪之后,描绘路径
            //grfx.DrawPath(new Pen(Brushes.Green, width1), path);

            //测试:第一个椭圆裁剪之后,填充路径
            //grfx.FillPath(Brushes.Black, path);

            //PathData pathdata = new PathData();
            //pathdata = path.PathData;
            //Console.WriteLine("使用PathData类型,{0}", pathdata.Points[0].X);
                        
            path.Reset();

            //PathData pathdata = new PathData();
            //pathdata = path.PathData;
            //Console.WriteLine("使用PathData类型,{0}", pathdata.Points[0].X);

            path.AddEllipse(cx/3,0,2 * cx/3,cy);
            //测试:添加第二个椭圆之后,描绘路径
            //grfx.DrawPath(new Pen(Brushes.Cyan, width1), path);

            //测试:添加第二个椭圆之后,填充路径
            //grfx.FillPath(Brushes.Black, path);

            

            //测试使用
            grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Purple, 0, 25);
            grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Purple, 0, 75);
            grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Purple, 0, 125);
            grfx.DrawLine(new Pen(Color.Purple, 5), cx,0, 0, cy);
            
            //测试路径的数组并输出数组
            Console.WriteLine(path.PathPoints.Length);
            Console.WriteLine(path.PointCount);
            for (int i = 0; i < path.PathPoints.Length; i++)
            {
                Console.WriteLine(path.PathPoints[i]);
                                
                grfx.DrawLine(new Pen(Color.Green),cx/2,cy/2,path.PathPoints[i].X,path.PathPoints[i].Y);
            }
            grfx.DrawLines(new Pen(Color.Green),path.PathPoints);
            //至此,以上均为测试内容

            //grfx.SetClip(path);

            //grfx.DrawPath(new Pen(Color.Violet), path);

            grfx.SetClip(path,(CombineMode)miCombineMode.Index);
            //测试:第二次裁剪之后,路径数组的数量
            Console.WriteLine("第二次裁剪之后,路径数组的数量是:{0}",path.PathPoints.Length);

            //测试:第二次裁剪之后,描绘路径
            //grfx.DrawPath(new Pen(Brushes.Blue, width1), path);  //注:此行代码中没有发现路径

            //测试:第二次裁剪之后,填充路径
            //grfx.FillPath(Brushes.Black, path);

            grfx.FillRectangle(Brushes.Yellow,0,0,cx,cy);
            
            grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!",Font,Brushes.Blue,0,150);
            grfx.DrawString("此行用于创建多行文字,用于测试界面的空间,验证相关内容的准确性!", Font, Brushes.Blue, 0, cy-15);
        }
    }
}

posted @ 2022-11-19 11:35  chenlight  阅读(327)  评论(0编辑  收藏  举报