最近用GDI+ 处理图形,裁减区域的时候发现一个古怪的现象,在网上又找不到解释,想来应该是M$的一个Bug, 姑且发在这里,供有类似经历的人参考。
现象:
假设有三个Rect, 分别为rt1, rt2, rt3。 求他们的合集区域。 使用Gdiplus::Region来处理的话。如下:
Gdiplus::Region reg; reg.MakeEmpty();
reg.Union(rt1); reg.Ungion(rt2); reg.Union(rt3);
通常情况下,会得到正确的结果。但是 如果 rt1 和 rt2 左右并列, 并且不相交! 而rt3(比较大 ) 和 rt1,rt2都相交的话, 则实际得到结果Region 会很古怪。 rt3的右边多出rt2的部分会被切掉一个rt1高度的矩形。
这个时候如果 移动rt1使他与rt2相交(哪怕一点点), 则得到的结果立刻正常。
如果保持rt1和rt2的位置(不相交),慢慢移动rt3, 一旦rt3只与其中一个相交,甚至都不相交,结果立刻正常。
这还不是最离奇的,更古怪的是,如果改变Union的顺序,使得大矩形rt3不在最后,结果也立刻正常。
演示代码如下: (GdiplusStartup & GdiplusShutdown 略)
#include “Gdiplus.h“
#pragma comment(lib, “Gdiplus.lib“)
using namespace Gdiplus;
...
CViwe::OnDraw(CDC* pDC)
{
Rect rt1(54, 107, 92, 77);
Rect rt2(168, 16, 92, 117);
Rect rt3(116, 70, 632, 150);
Region reg;
reg.MakeEmpty();
reg.Union(rt1);
reg.Union(rt2);
reg.Union(rt3);
Graphics g(pDC->GetSafeHdc());
SolidBrush brush(Color(128, 255, 0, 0));
g.FillRegion(&brush, ®);
}
这样子会得到古怪的结果,如果把 reg.Union(rt3)放到reg.Union(rt2)的前面,结果就是正确的。
试过了Union的其他版本,效果一样。
如果不是用GDI+, 而使用传统的RGN, 则结果无论怎样都是正确的。
Bug重现要点:
rt1 和 rt2 左右并排,并且留有间隙。
rt3 较宽,高度没什么问题,不过高度较高的话容易看出效果。
rt3 和 rt1, rt2都重叠一部分,并且右边还超出rt2。 右边超出越长效果越明显。
rt3左边跃不跃过rt1左边都无所谓,不跨越的话容易看出效果。
最重要的是, Union的时候,最大的rt3放在最后,才会出现古怪的效果。
C# 也出现一模一样的Bug. 演示代码如下:
private void OnPaint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Rectangle rt1 = new Rectangle(54, 107, 92, 77);
Rectangle rt2 = new Rectangle(168, 16, 92, 117);
Rectangle rt3 = new Rectangle(40 , 70, 632, 150);
Region reg = new Region();
reg.MakeEmpty();
reg.Union(rt1);
reg.Union(rt2);
reg.Union(rt3);
SolidBrush brush = new SolidBrush(Color.Blue);
e.Graphics.FillRegion(brush, reg);
}
把reg.Union(rt3)调到前面也能恢复正常。
结论是:
rt3 被不正常切掉的矩形 高度和位置 是 rt1 和 rt2 在高度上的相交部分,宽度是从rt2的右边开始,一直到rt3的右边结束。
多于2个矩形横着排开,中间留有间隙,效果更复杂。每一个左边的矩形都会对右边产生相应的切割作用。