Linear Gradient Brushes

The remaining two brush classes are LinearGradientBrush and PathGradientBrush; the word gradient here refers to a transition between colors. LinearGradientBrush involves a transition between two colors, sometimes called a fountain. At first, it may seem complicated to define a way in which one color merges with another, and that's probably why there are a couple different ways of specifying such a brush.

A gradient between two colors can be defined by a pair of parallel lines. Each line is a pure color. The color makes a transition between the two lines. Here's an example with the first color being Color.LightGray and the second Color.DarkGray:

The linear-gradient brush is thus an infinitely long stripe with two parallel borders of two colors.

To define such a brush, you don't need to specify two parallel lines. It's much easier to specify two points. The two parallel borders are at right angles to the line connecting the two points:

Note that there are an infinite number of pairs of points that result in the same linear gradient. I'm going to refer to the line connecting those two points as the gradient line. I also want to define the term mix line as the line at right angles to the gradient line and parallel to the two border lines.

The LinearGradientBrush class has eight constructors. Two of these let you specify two points and two colors:

LinearGradientBrush Constructors (selection)

LinearGradientBrush(Point ptl, Point pt2, Color clrl, Color clr2) LinearGradientBrush(PointF ptfl, PointF ptf2, Color clrl, Color clr2)

The only difference between these two constructors is the use of either Point or PointF structures. The points are in world coordinates. The color at the first point (pt1 or ptfl) is clrl, and the color at the second point (pt2 or ptf2) is clr2.

Let's take a look at a program that creates a LinearGradientBrush object in its DoPage method, defining the first point as (cx/4, cy/4) and the second as (3*cx/4, 3*cy/4). The two colors are Color.White and Color.Black. The program then colors a rectangle the size of its display area with this brush.

TwoPointLinearGradientBrush.es

//----------------------------------------------------------

// TwoPointLinearGradientBrush.es ® 2001 by Charles Petzold //----------------------------------------------------------

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

class TwoPointLinearGradientBrush: PrintableForm {

public new static void Main()

Application.Run(new TwoPointLinearGradientBrush());

TwoPointLinearGradientBrush()

Text = "Two-Point Linear Gradient Brush";

protected override void DoPage(Graphics grfx, Color clr, int cx, int

LinearGradientBrush lgbrush = new LinearGradientBrush(

new Point(cx / 4, cy / 4), new Point(3 * cx / 4, 3 * cy / 4)

Color.White, Color.Black);

grfx.FillRectangle(lgbrush, 0, 0, cx, cy);

I haven't yet mentioned what happens outside the stripe that the LinearGradientBrush object defines. As you can see, by default the brush is tiled:

The wide continuous stripe from the lower left to the upper right is defined by the two brush coordinates. On either side of the stripe (in this case, the upper left and lower right of the client area), the stripe is repeated.

This behavior is controlled by the WrapMode property of the brush. WrapMode.Tile (the default) is the same as WrapMode.TileFlipY, and it causes the brush to be tiled with no flipping, as shown previously.

WrapMode.TileFlipX is the same as Wrapmode.TileFlipXY and causes the brush to be flipped so that there are no discontinuities, like so:

WrapMode.Clamp is not allowed for linear-gradient brushes.

Let me emphasize again that any Fill method you call with a particular brush essentially provides only a window through which you view the brush. When using texture brushes or gradient brushes, the appearance of any filled area depends to some degree on where the area is drawn. If you draw a small rectangle using the brush defined by the TwoPointLinearGradientBrush program, it might not even seem like much of a gradient.

In many cases, you'll define a particular linear-gradient brush based on the actual coordinates of the object you're filling. For example, if you want to fill a rectangle with a linear-gradient brush, you'll define the brush with the same coordinates you use to draw the rectangle. In such cases, you might find it convenient to use the following constructors for LinearGradientBrush that have a rectangle argument:

LinearGradientBrush Constructors (selection)

LinearGradientBrush(Rectangle rect, Color clrl, Color clr2,

LinearGradientMode lgm) LinearGradientBrush(RectangleF rectf, Color clrl, Color clr2, LinearGradientMode lgm)

The LinearGradientMode enumeration defines how the gradient line is formed from the sides or corners of the rectangle:

LinearGradientMode Enumeration

Member

Value

Description

Horizontal

0

Gradient line is horizontal, clrl at left side, clr2 at right

Vertical

1

Gradient line is vertical, clrl at top side, clr2 at bottom

ForwardDiagonal

2

Mix line passes through upper right and lower left corners; upper left corner is clrl and lower right is clr2

BackwardDiagonal

3

Mix line passes through upper left and lower right corners; upper right corner is clrl and lower left is clr2

Notice that for the last two enumeration values, two opposite corners of the rectangle define the mix line rather than the gradient line. Although the two other corners of the rectangle are pure colors and the border lines pass through those two corners, those two corners do not define the gradient line unless the rectangle is a square. Let's take a closer look.

The following program defines a linear-gradient brush based on a rectangle that is half the width and height of the display area, and centered within the display area. You can use the menu to set the constructor's LinearGradientMode argument. After filling the display area with that brush, the DoPage method also draws the rectangle used in creating the brush.

ReetangleLinearGradientBrush.es

//-----------------------------------------------------------

// ReetangleLinearGradientBrush.es ® 2001 by Charles Petzold //-----------------------------------------------------------

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

elass ReetangleLinearGradientBrush: PrintableForm {

Menultem miCheeked;

publie new statie void Main() {

Applieation.Run(new ReetangleLinearGradientBrush());

publie ReetangleLinearGradientBrush() {

Text = "Reetangle Linear-Gradient Brush"; Menu = new MainMenu();

Menu.Menultems.Add("&Gradient-Mode");

foreaeh (LinearGradientMode gm in

Enum.GetValues(typeof(LinearGradientMode)))

Menultem mi = new Menultem(); mi.Text = gm.ToString();

mi.Cliek += new EventHandler(MenuGradientModeOnCliek); Menu.MenuItems[0].MenuItems.Add(mi);

miCheeked = Menu.Menultems[0].MenuItems[0]; miCheeked.Cheeked = true;

void MenuGradientModeOnCliek(objeet obj, EventArgs ea) {

miCheeked.Cheeked = false; miCheeked = (Menultem) obj; miCheeked.Cheeked = true;

Invalidate();

protected override void DoPage(Graphics grfx, Color clr, int cx, int cy)

Rectangle rectBrush =

LinearGradientBrush lgbrush = new LinearGradientBrush(

rectBrush, Color.White, Color.Black, (LinearGradientMode) miChecked.Index);

grfx.FillRectangle(lgbrush, 0, 0, cx, cy); grfx.DrawRectangle(Pens.Black, rectBrush);

Here's an example when you've used the menu to select LinearGradientMode.ForwardDiagonal and the window has been widened somewhat:

Although the upper left corner of the brush rectangle is colored with the first color and the lower right corner with the second color, the gradient line is obviously not the line from the upper left corner to the lower right corner because the gradient line is always at right angles to the border lines. Instead, the mix line (parallel to the border lines and midway between them) passes through the upper right and lower left corners of the rectangle.

The four final constructors for LinearGradientBrush let you specify a rectangle and an angle: LinearGradientBrush Constructors (selection)

LinearGradientBrush (Rectangle rect, Color clrl, Color clr2, float fAngle) LinearGradientBrush (Rectangle rect, Color clrl, Color clr2, float fAngle, bool bScale)

LinearGradientBrush (RectangleF rectf, Color clrl, Color clr2, float fAngle)

LinearGradientBrush (RectangleF rectf, Color clrl, Color clr2, float fAngle, bool bScale)

If fAngle is 0, the effect is identical to LinearGradientMode.Horizontal: the gradient line is horizontal from the left side of the rectangle to the right side.

As fAngle increases, the gradient line rotates clockwise that number of degrees. The upper left corner of the rectangle is the first color, and the lower right corner is the second color. When fAngle reaches 90 degrees, the effect is identical to LinearGradientMode.Vertical: the gradient line is vertical from the top of the rectangle to the bottom. As fAngle increases beyond 90 degrees, the gradient line continues to rotate clockwise. But now the upper right corner of the rectangle is the first color, and the lower left corner of the rectangle is the second color.

The optional bScale argument indicates whether the rotation angle is scaled by any transform associated with the brush.

I've already alluded to the WrapMode and Transform properties of LinearGradientBrush. This table of four properties also includes the rectangle specified (or implied) by the constructor and an array of two colors used in the brush:

LinearGradientBrush Properties (selection)

Type

Property

Accessibility

RectangleF

Rectangle

get

Color[]

LinearColors

get/set

WrapMode

WrapMode

get/set

Matrix

Transform

get/set

In addition to duplicating the TranslateTransform, ScaleTransform, RotateTransform, MultiplyTransform, and ResetTransform methods defined in the TextureBrush class, the LinearGradientBrush class also includes these two methods:

+1 0

Post a comment