Enhancing JPEG, PNG, Text Using C# .NET Part 3 - Glow Effect
The Glow effect inflates all non-transparent areas of the image by the specified amount, then applies the Gaussian blur effect to make the borders smooth. The resulting image is used as the background when drawing the source image once again.
Ready to Get Started? Download Document Solutions for Imaging Today!
The Glow effect can be found in the set of available MS Word effects to be applied to text or shapes. For example:
Let’s see how to achieve the same effect when drawing text and graphics to a GcBitmap.
For example, create a C# Console App for .NET 7.0, then add a reference to GrapeCity.Documents.Imaging NuGet package and the following code to create an image that follows:
using System.Drawing;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Text;
using var bmp = new GcBitmap(1120, 300, false);
using var g = bmp.CreateGraphics(Color.DarkGray);
g.Renderer.SlowAntialiasing = true;
var gp = PrepareFigurePath();
g.Renderer.FillPath(gp, Color.DarkOrchid);
var tl = PrepareTextLayout(g);
g.DrawTextLayout(tl, new PointF(380, 50));
bmp.SaveAsPng("image1.png");
static GraphicsPath PrepareFigurePath()
{
var pb = new PathBuilder();
pb.BeginFigure(50, 175);
pb.AddLine(105, 155);
var arc = new ArcSegment
{
Size = new SizeF(91, 86),
SweepDirection = SweepDirection.Clockwise,
Point = new PointF(275, 102),
};
pb.AddArc(arc);
pb.AddLine(325, 85);
pb.AddLine(340, 125);
pb.AddLine(287, 142);
arc.Point = new PointF(120, 195);
pb.AddArc(arc);
pb.AddLine(65, 215);
pb.EndFigure(true);
pb.Figures.Add(new EllipticFigure(new RectangleF(147, 98, 100, 95)));
return pb.ToPath();
}
static TextLayout PrepareTextLayout(GcGraphics g)
{
var tl = g.CreateTextLayout();
var f1 = new TextFormat
{
FontName = "Calibri",
FontSize = 160,
FontSizeInGraphicUnits = true,
ForeColor = Color.DarkOrchid,
FontBold = true
};
var f2 = new TextFormat(f1)
{
ForeColor = Color.White,
StrokePen = new Pen(Color.DarkOrchid, 3)
};
tl.Append("Grape", f1);
tl.Append("City", f2);
return tl;
}
Now, we’d like to add some glow. We need to draw the text and graphics on a transparent bitmap at first:
using var bmp1 = new GcBitmap(1120, 300, false);
using (var g = bmp1.CreateGraphics(Color.Transparent))
{
g.Renderer.SlowAntialiasing = true;
var gp = PrepareFigurePath();
g.Renderer.FillPath(gp, Color.DarkOrchid);
var tl = PrepareTextLayout(g);
g.DrawTextLayout(tl, new PointF(380, 50));
}
As you see, we passed Color.Transparent to the CreateGraphics method to clear the GcBitmap before drawing any graphics. The image in GcBitmap (bmp1) looks as follows at the current stage:
The next step is converting the image to a transparency mask:
using var gs = bmp1.ToGrayscaleBitmap(ColorChannel.Alpha);
If we draw 0xFF values (opaque pixels) as white and 0x00 values (transparent pixels) as black, the GrayscaleBitmap looks like this:
Now, let’s call the new ApplyGlow method of GrayscaleBitmap (6 is the inflation radius, 9 is the blur radius):
gs.ApplyGlow(6, 9);
The transparency mask in GrayscaleBitmap changes as follows:
Now we can convert the transparency mask to a GcBitmap drawing opaque pixels with the glow color (Yellow) and applying some additional transparency:
using var bmp2 = gs.ToShadowBitmap(Color.Yellow, 0.8f);
The image in bmp2 looks like this:
The next step is filling the background. Let’s use Color.Gray as the background instead of Color.DarkGray because the Gray is actually darker than DarkGray:
bmp2.ConvertToOpaque(Color.Gray);
The background GcBitmap (bmp2) now looks like this:
We are ready for the final step - drawing the foreground bitmap over the prepared background bitmap:
bmp2.AlphaBlend(bmp1, 0, 0);
The resulting image in bmp2 looks like this:
There is also an alternative approach when we use just one GcBitmap and draw the text and graphics twice: on the transparent background at first and the prepared background with glow at last. That approach consumes less memory but more time because text/graphics rasterization takes longer than simple alpha blending.
An example of code applying different glow to different parts of the image:
using System.Drawing;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Text;
// draw to a bitmap with transparent background
using var bmp = new GcBitmap(1120, 300, false);
using (var g = bmp.CreateGraphics(Color.Transparent))
{
g.Renderer.SlowAntialiasing = true;
var gp = PrepareFigurePath();
g.Renderer.FillPath(gp, Color.DarkOrchid);
var tl = PrepareTextLayout(g);
g.DrawTextLayout(tl, new PointF(380, 50));
}
// clip the first rectangle (no intersection with the second)
var rect1 = new Rectangle(20, 20, 345, 250);
using var bmp1 = bmp.Clip(rect1);
using (var gs = bmp1.ToGrayscaleBitmap(ColorChannel.Alpha))
{
gs.ApplyGlow(10, 20);
gs.ToShadowBitmap(bmp1, Color.Aquamarine, 0.6f);
}
// clip the second rectangle (no intersection with the first)
var rect2 = new Rectangle(365, 20, 700, 240);
using var bmp2 = bmp.Clip(rect2);
using (var gs = bmp2.ToGrayscaleBitmap(ColorChannel.Alpha))
{
gs.ApplyGlow(6, 9);
gs.ToShadowBitmap(bmp2, Color.MistyRose, 0.6f);
}
// clone the metadata of the source bitmap
using var res = bmp.Clone(false, true);
res.Clear(0x00FFFFFFu);
// use res.AlphaBlend if the partial images can intersect
res.BitBlt(bmp1, rect1.X, rect1.Y);
res.BitBlt(bmp2, rect2.X, rect2.Y);
res.ConvertToOpaque(Color.Gray);
res.AlphaBlend(bmp, 0, 0);
res.SaveAsPng("image3.png");
static GraphicsPath PrepareFigurePath()
{
var pb = new PathBuilder();
pb.BeginFigure(50, 175);
pb.AddLine(105, 155);
var arc = new ArcSegment
{
Size = new SizeF(91, 86),
SweepDirection = SweepDirection.Clockwise,
Point = new PointF(275, 102),
};
pb.AddArc(arc);
pb.AddLine(325, 85);
pb.AddLine(340, 125);
pb.AddLine(287, 142);
arc.Point = new PointF(120, 195);
pb.AddArc(arc);
pb.AddLine(65, 215);
pb.EndFigure(true);
pb.Figures.Add(new EllipticFigure(new RectangleF(147, 98, 100, 95)));
return pb.ToPath();
}
static TextLayout PrepareTextLayout(GcGraphics g)
{
var tl = g.CreateTextLayout();
var f1 = new TextFormat
{
FontName = "Calibri",
FontSize = 160,
FontSizeInGraphicUnits = true,
ForeColor = Color.DarkOrchid,
FontBold = true
};
var f2 = new TextFormat(f1)
{
ForeColor = Color.White,
StrokePen = new Pen(Color.DarkOrchid, 3)
};
tl.Append("Grape", f1);
tl.Append("City", f2);
return tl;
}
The resulting image in image3.png:
Suppose some parts with different glow can intersect. In that case, drawing each part to a separate bitmap might be handy, then creating a couple of glow bitmaps and using the AlphaBlend method to compose the glow bitmaps to the target transparent bitmap before converting its background to opaque.
The GrayscaleBitmap.ApplyGlow method is at the core of the described algorithm. It is also used in the algorithm applying the Soft Edges effect (if the infRadius parameter is negative):
/// <summary>
/// When applied to a grayscale mask built from a color image using
/// the <see cref="GcBitmap.ToGrayscaleBitmap"/> method,
/// modifies it so that the result can be used as a transparency mask
/// to produce <b>Glow</b> or <b>Soft Edges</b> effect.
/// </summary>
/// <param name="infRadius">The radius of inflation (positive, glow)
/// or deflation (negative, soft edges), in pixels.</param>
/// <param name="blurRadius">The radius of Gaussian blur, in pixels.</param>
public void ApplyGlow(int infRadius = 4, int blurRadius = 6)
Applying the Glow effect might be slow if the size of the image and/or the radius of inflation are too large.
MESCIUS' .NET Imaging API Library
This article only scratches the surface of the full capabilities of Document Solutions for Imaging (DsImaging, previously GcImaging). Review our documentation to see the many available features and our demos to see the features in action with downloadable sample projects.
Integrating this .NET imaging server-side API into a desktop or web-based application allows developers to programmatically create and manipulate images at scale and load, process, and save images across many different formats.
Ready to Get Started? Download Document Solutions for Imaging Today!