Coding Life

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

最近被这个问题搞的我很无语,不多说,直接贴代码了:

测试函数:

void test_gdiplus_SetBlendBellShape(HDC hdc, RECT* pRect, REAL angle = 0.0f)
{
	Graphics myGraphics(hdc);

	Rect rt(pRect->left, pRect->top, pRect->right-pRect->left, pRect->bottom-pRect->top);
	
	LinearGradientBrush linGrBrush( 
		rt,
		Color(255, 255, 0, 0),  // red
		Color(255, 0, 0, 255),  // blue
		angle);

	linGrBrush.SetBlendBellShape(1.0f);	// this line will make one extra "border" appear
	
	myGraphics.FillRectangle(&linGrBrush, rt);
}

调用:

#define MWIDTH	102
#define MHEIGHT	100
void CXXXDlg::OnPaint()
{
	CPaintDC dc(this);

	CRect rt;
	rt.left = 50;
	rt.top = 50;
	rt.right = rt.left + MWIDTH;
	rt.bottom = rt.top + MHEIGHT;

	test_gdiplus_SetBlendBellShape(dc.GetSafeHdc(), rt);
}

出来的效果如下(运行环境 VC6 + XP SP3):

放大的效果:

  可以看到有一条多余的蓝边出现在填充矩形的左侧,如果把 SetBlendBellShape 的调用注释掉则不会出现,按道理1.0就应该是等价于默认的设置,所以怀疑这是GDI+的bug

 

  后来在测试过程中发现即使不调用 SetBlendBellShape,填充矩形在某些 size 下也会出现这条额外的边,比如在上述code中的size 102x100,简单的说这个问题还跟填充区域的大小甚至位置有关。

 

  就这个问题Google了一下,找到一些references:
1) http://blog.csdn.net/mubingyun/archive/2008/12/09/3484260.aspx
2) http://www.codeproject.com/KB/GDI-plus/TurnThePage.aspx

 

  弄了很久后发现要解决这个问题,貌似有几个解决方法:

1、调用 Graphics::SetPixelOffsetMode( PixelOffsetModeHalf ),好像在某些case下还是不work。
2、使 brush 的矩形比填充的矩形大一个像素单位。
3、调用  LinearGradientBrush::SetWrapMode( WrapModeTileFlipXY ),实际就是重新拿第一个颜色去覆盖那条多余的蓝边。

看来GDI+作为Bleeding Edge的技术就是会让使用者承担风险啊。


1/18/2010(在发布此随笔之前),在搜索了一天后找到一些资料:

1) http://stackoverflow.com/questions/110081/lineargradientbrush-artifact-workaround
2) http://www.experts-exchange.com/Microsoft/Development/.NET/Visual_CSharp/Q_23329115.html
3) http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.languages.csharp/2007-01/msg01592.html

 

你会看到有人(包括一个MS MVP)说这是一个已知的bug,并且建议说把brush弄得比要填充的矩形大一点(一个像素即可)可以解决问题。

但这样是不是真的就是最佳workaround了呢?看这个发帖提问的人的回复:

I had tried inflating the brush, and it seemed to eliminate the random
line. But it introduced its own problems:

Some of the shaded areas get as narrow as 3 pixels (perspective-shrink
during animation). That means that a 1-pixel miss results in a very
visible 33% change in the outer color. This is not as ugly as the
black lines, but still a noticeable glitch.

And with the inflated brush, there's now no way to get a pure white or
pure black row of pixels on the outer edge of a 3 pixel wide object,
as the crossfade is already at 20% (1 pixel out of 5) when the brush
hits the object edge. But then the bug will pop in and randomly draw a
pure white or black edge.

 

主要意思是说这个方法引入了其他问题,比如对比增大brush的矩形之前,终点的颜色不太一样了,更重要的是在矩形比较小的时候这个问题还是会出现:

7x100

  上面是在 位置249,249 大小为7x100的时候的截图

 

就我看来,LinearGradientBrush::SetWrapMode( WrapModeTileFlipXY ) 虽然有点tricky,但至少目前都还没发现问题重现,看来这暂时是个最终解决方法了。

 

顺便贴下某人的complaints(来源: http://weblogs.mozillazine.org/tor/archives/2005/06/):

I'm considering switching the renderer that Mozilla SVG uses on win32 to cairo from GDI+. The reasons for this:

  • Less problems for the user, as they don't have to deal with downloading GDI+ if they're on an older system (pre-XP).
  • Some bugs in GDI+ are unfixable (or at least, would take a lot of effort). For example the spread method for radial gradients, bug 296411.
  • Consistent behavior between platforms, up to the quality of the respective cairo backends.
  • Saving effort adding features for GDI+, which likely won't be used past 1.8.x. The patch for <svg:textPath>, bug 282579, is an example of this.

注意第二点,"Some bugs in GDI+ are unfixable",that is so microsoft, heh?

posted on 2010-01-20 21:29  yonken  阅读(1242)  评论(0编辑  收藏  举报