views:

147

answers:

2

I'd like to know how I create a vignetting effect on a picture using C# and .NET.

Does anyone have any ideas how to do this? Or are there any resources that will have the algorithm already done for me?

+2  A: 

If your picture is in a file, and if that is fast enough for your requirements, you can use the command line tool convert of ImageMagick, it has an option -vignette. To call this from within a C# program, you can run it via System.Diagnostics.Process.Start, or you use this .NET wrapper for ImageMagick.

Doc Brown
Thanks, that's a good solution for my problem.However, I would like to see the algorithm behind this effect in order to be able to implement it myself.
Olivier PAYEN
+4  A: 

I believe this will do what you want:

public void PaintVignette(Graphics g, Rectangle bounds)
{
    Rectangle ellipsebounds = bounds;
    ellipsebounds.Offset(-ellipsebounds.X, -ellipsebounds.Y);
    int x = ellipsebounds.Width - (int)Math.Round(.70712 * ellipsebounds.Width);
    int y = ellipsebounds.Height - (int)Math.Round(.70712 * ellipsebounds.Height);
    ellipsebounds.Inflate(x, y);

    using (GraphicsPath path = new GraphicsPath())
    {
        path.AddEllipse(ellipsebounds);
        using (PathGradientBrush brush = new PathGradientBrush(path))
        {
            brush.WrapMode = WrapMode.Tile;
            brush.CenterColor = Color.FromArgb(0, 0, 0, 0);
            brush.SurroundColors = new Color[] { Color.FromArgb(255, 0, 0, 0) };
            Blend blend = new Blend();
            blend.Positions = new float[] { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0F };
            blend.Factors = new float[] { 0.0f, 0.5f, 1f, 1f, 1.0f, 1.0f };
            brush.Blend = blend;
            Region oldClip = g.Clip;
            g.Clip = new Region(bounds);
            g.FillRectangle(brush, ellipsebounds);
            g.Clip = oldClip;
        }
    }
}

public Bitmap Vignette(Bitmap b)
{
    Bitmap final = new Bitmap(b);
    using (Graphics g = Graphics.FromImage(final)) {
        PaintVignette(g, new Rectangle(0, 0, final.Width, final.Height));
        return final;
    }
}

What's going on here? First I wrote code that would fill a rectangle with an elliptical gradient brush that went from white to black. Then I modified the code so that the filled area would also include the corners. I did this by increasing the rectangle size by the difference between the rectangle dimensions and sqrt(2)/2 * the rectangle dimensions.

Why sqrt(2)/2? Because the point (sqrt(2)/2, sqrt(2)/2) is the 45 degree angle point on a unit circle. Scaling by the width and height gives the distance needed to inflate the rect to make sure it's fully covered.

Then I adjusted the Blend of the gradient to be much more white in the center.

Then I changed the color from white to pure transparent black and from black to pure opaque black. This has the effect of painting the far corners black and shade less on the way in to the center.

Finally, I wrote a utility method that runs on a Bitmap (I haven't tested this part - I tested the code on a graphics from a Panel, but I think it will work here too.

plinth
Thanks a lot, I'll try that
Olivier PAYEN