关于Graphics.DrawRectangle的右侧及下侧边界

.Net技术 码拜 8年前 (2016-05-08) 2104次浏览
以前都是用C++调用GDI画图,其中画矩形的API:Retangle中明确说明:The rectangle that is drawn excludes the bottom and right edges。这次编程改用C#,调用Graphics.DrawRectangle时发现总是多出一行一列。随便找个窗口测试了一下:

	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

引用:

…用C#,调用Graphics.DrawRectangle时发现总是多出一行一列…
…这个似乎很愚蠢。但Graphics.FillRectangle却不存在这个问题…

不清楚Graphics为什么这样设计。有谁能说说吗?

简单的说,这是原因是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去边适用于整点像素,它具体的行为结果容易理解。
而对浮点就不适合了,浮点去边没有好的定义。
关于Graphics.DrawRectangle的右侧及下侧边界


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明关于Graphics.DrawRectangle的右侧及下侧边界
喜欢 (0)
[1034331897@qq.com]
分享 (0)