GDI+ Region 的 古怪Bug.

最近用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);
}

这样子会得到古怪的结果,如果把 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个矩形横着排开,中间留有间隙,效果更复杂。每一个左边的矩形都会对右边产生相应的切割作用。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值