Vignettes in DotImage
On Stack Overflow, a user asked how to create vignettes in .NET. I wrote up this answer, which explains the math and the general approach.
To give you an idea of what this looks like, here is the output of an application which applies the vignette to an image. This is different from the Stack Overflow answer in that I chose to do this in an ImageRegionCommand from within dotImage.
To do this, I created a new class called VignetteCommand, overrode four members and added one utility method. This command works on 24 bit RGB, 32 bit RGB, and 32 bit RGBA which a limitation imposed by GDI+. Since this command works by painting on top of an existing image, InPlaceProcessing will be overridden to return true always. In theory, we could do without, but we’d just be copying the existing image onto the new image, then painting. We override VerifyProperties to make sure that any user set properties are right. Finally, we override PerformActualCommand to do the actual work. All the rest of the infrastructure is handled by dotImage.
Here is the code:
using System;
using Atalasoft.Imaging;
using Atalasoft.Imaging.ImageProcessing;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Vignette1
{
public class VignetteCommand : ImageRegionCommand
{
private PixelFormat[] _supportedPixelFormats = new PixelFormat[] {
PixelFormat.Pixel24bppBgr, PixelFormat.Pixel32bppBgr, PixelFormat.Pixel32bppBgra
};
public override PixelFormat[] SupportedPixelFormats { get { return _supportedPixelFormats; } }
public override bool InPlaceProcessing { get { return true; } }
protected override void VerifyProperties(AtalaImage image)
{
if (Positions == null && Factors == null)
return;
if (Positions == null || Factors == null)
throw new ArgumentException(Positions == null ? "Positions" : "Factors");
if (Positions.Length != Factors.Length)
throw new ArgumentException("position and length arrays must be the same length", "Positions");
}
protected override AtalaImage PerformActualCommand(AtalaImage source, AtalaImage dest, System.Drawing.Rectangle imageArea, ref ImageResults results)
{
using (Graphics g = source.GetGraphics())
{
PaintVignette(g, imageArea);
}
return null;
}
private 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();
float[] positions = Positions;
float[] factors = Factors;
if (positions == null || factors == null)
{
positions = new float[] { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0F };
factors = new float[] { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.0f };
}
blend.Positions = positions;
blend.Factors = factors;
brush.Blend = blend;
Region oldClip = g.Clip;
g.Clip = new Region(bounds);
g.FillRectangle(brush, ellipsebounds);
g.Clip = oldClip;
}
}
}
public float[] Positions { get; set; }
public float[] Factors { get; set; }
}
}