LinearGradientBrush Artifact Workaround?

2020-07-06 07:07发布

The LinearGradientBrush in .net (or even in GDI+ as a whole?) seems to have a severe bug: Sometimes, it introduces artifacts. (See here or here - essentially, the first line of a linear gradient is drawn in the endcolor, i.e. a gradient from White to Black will start with a Black line and then with the proper White to Black gradient)

I wonder if anyone found a working workaround for this? This is a really annoying bug :-(

Here is a picture of the Artifacts, note that there are 2 LinearGradientBrushes:

alt text http://img142.imageshack.us/img142/7711/gradientartifactmm6.jpg

标签: c# gdi+
5条回答
迷人小祖宗
2楼-- · 2020-07-06 07:24

At least with WPF you could try to use GradientStops to get 100% correct colors right at the edges, even when overpainting.

查看更多
小情绪 Triste *
3楼-- · 2020-07-06 07:28

You can use the nice Inflate(int i) method on a rectangle to get the bigger version.

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2020-07-06 07:29

I have noticed this as well when using gradient brushes. The only effective workaround I have is to always create the gradient brush rectangle 1 pixel bigger on all edges than the area that is going to be painted with it. That protects you against the issue on all four edges. The downside is that the colors used at the edges are a fraction off those you specify, but this is better than the drawing artifact problem!

查看更多
Explosion°爆炸
5楼-- · 2020-07-06 07:40

I would finesse Phil's answer above (this is really a comment but I don't have that privilege). The behaviour I see is contrary to the documentation, which says:

The starting line is perpendicular to the orientation line and passes through one of the corners of the rectangle. All points on the starting line are the starting color. Then ending line is perpendicular to the orientation line and passes through one of the corners of the rectangle. All points on the ending line are the ending color.

Namely you get a single pixel wrap-around in some cases. As far as I can tell (by experimentation) I only get the problem when the width or height of the rectangle is odd. So to work around the bug I find it is adequate to increase the LinearGradientBrush rectangle by 1 pixel if and only if the dimension (before expansion) is an odd number. In other words, always round the brush rectangle up the the next even number of pixels in both width and height.

So to fill a rectangle r I use something like:

Rectangle gradientRect = r;
if (r.Width % 2 == 1)
{
    gradientRect.Width += 1;
}
if (r.Height % 2 == 1)
{
    gradientRect.Height += 1;
}
var lgb = new LinearGradientBrush(gradientRect, startCol, endCol, angle);
graphics.FillRectangle(lgb, r);

Insane but true.

查看更多
家丑人穷心不美
6楼-- · 2020-07-06 07:42

I experienced artifacts too in my C++ code. What solved the problem is setting a non-default SmoothingMode for the Graphics object. Please note that all non-default smoothing modes use coordinate system, which is bound to the center of a pixel. Thus, you have to correctly convert your rectangle from GDI to GDI+ coordinates:

    Gdiplus::RectF      brushRect;

    graphics.SetSmoothingMode( Gdiplus::SmoothingModeHighQuality );

    brushRect.X = rect.left - (Gdiplus::REAL)0.5;
    brushRect.Y = rect.top - (Gdiplus::REAL)0.5;
    brushRect.Width = (Gdiplus::REAL)( rect.right - rect.left );
    brushRect.Height = (Gdiplus::REAL)( rect.bottom - rect.top );

It seems like LinearGradientBrush works correctly only in high-quality modes.

查看更多
登录 后发表回答