protected override void OnPaint(PaintEventArgs pe) { base.OnPaint(pe); pe.Graphics.DrawRectangle(new Pen(Color.Red), ClientRectangle); }
果然,右边跟下边不见了。这个似乎很愚蠢。但Graphics.FillRectangle却不存在这个问题。
举个具体的例子可能看得明白些:
假定一个矩形左上角坐标是(0,0),宽度和高度是5,那么这个矩形在横向覆盖的像素应该是0、1、2、3、4(共5个),纵向同样。使用GDI的Retangle,左右两条边分别画在像素0和像素4处,而Graphics.DrawRectangle却画在像素0和像素5处!这显然不合理(原因是这种处理方法对于左右两侧是不对称的)。同时,Graphics.FillRectangle的覆盖范围却是像素0~4(不包括5)。于是下列代码看起来很美,结果却很糟(露了两条缝):
void DrawFilledRectangle(Graphics g, Rectangle rect, Color clrBorder, Color clrFill) { g.DrawRectangle(new Pen(clrBorder), rect); rect.Inflate(-1, -1); g.FillRectangle(new SolidBrush(clrFill), rect); }
不清楚Graphics为什么这样设计。有谁能说说吗?
40
简单的说,这是原因是DrawRectangle的时候要画边框,实际面积要比Rectangle多一些。
而FillRectangle,则只是填充Rectangle的实际面积。
假设一个矩形(1,1,4,4),它的长宽各为4,理论面积为16,位置为(1,1)。
那么填充的时候,需要16个像素。
而当我们画矩形的时候,需要线条,假设线条的粗细为1。公平起见,线条的一半落在矩形里,线条的一半落在矩形外。
因此,这时含边框的矩形,实际占有了25个象素的面积,比理论矩形,长宽各多出了一个像素。
为什么多出的一个像素,要偏在右下方呢?
GDI+也想公平地画在的中间。但是,这就带来了骑墙的半个象素怎么样处理的问题。
因此,默认上GDI+就采用了最快的取整方法。也就造成你观察到的“右下边框不见”的现象。
实际上,GDI+也可以用偏移0.5的处理方法,并用抗锯齿的方式来展现半个像素。
因此,你这样画,既可以见到边框了:
protected override void OnPaint(PaintEventArgs pe) { base.OnPaint(pe); pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // 抗锯齿 pe.Graphics.PixelOffsetMode = PixelOffsetMode.Half; // 偏移0.5 pe.Graphics.DrawRectangle(new Pen(Color.Red), ClientRectangle); }
至于为什么GDI+不用GDI的exclusive的方法,至少有一个原因:
GDI+里面的单位,可以使浮点,而且并不于限定于像素(还可以用毫米,英寸计量等等)。
exclusive去边适用于整点像素,它具体的行为结果容易理解。
而对浮点就不适合了,浮点去边没有好的定义。