Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

.pdf
Скачиваний:
106
Добавлен:
16.08.2013
Размер:
29.8 Mб
Скачать

1018 C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

In this example, the FillRectangle() method doesn’t quite fill the entire bitmap. Instead, it leaves a border 1-pixel wide all around. Because you haven’t painted any content to this area, these pixels will have the default color (which, for a bitmap that you render to the GIF format, is black).

The next portion of the drawing code renders a static label message. To do this, you need to create a System.Drawing.Font object that represents the font you want to use. This shouldn’t be confused with the FontInfo object you use with ASP.NET controls to specify the requested font for a web page. Unlike FontInfo, Font represents a single, specific font (including typeface, size, and style) that’s installed on the current computer. When you create a Font object, you specify the font name, point size, and style, as shown here:

Font font = new Font("Impact", 20, FontStyle.Regular);

Tip Because this image is generated on the server, you can use any font that the server has installed when creating the graphic. The client won’t need to have the same font, because the client receives the text as a rendered image.

To render the text, you use the DrawString() method of the Graphics object. As with the FillRectangle() object, you need to specify the coordinates where the drawing should begin. This point represents the top-left corner of the text block. In this case, the point (10, 5) is used, which gives a distance of 10 pixels from the left and 5 pixels from the top.

g.DrawString("This is a test.", font, Brushes.Blue, 10, 5);

Once the image is complete, you can send it to the browser using the Image.Save() method. Conceptually, you “save” the image to the browser’s response stream. It then gets sent to the client and is displayed in the browser. When you use this technique, your image replaces any other webpage data and bypasses the web control model.

// Render the image to the output stream. image.Save(Response.OutputStream,

System.Drawing.Imaging.ImageFormat.Gif);

Tip You can save an image to any valid stream, including a FileStream. This technique allows you to save dynamically generated images to disk, so you can use them later in other web pages.

Finally, you should explicitly release your image and graphics context when you’re finished, because both hold onto some unmanaged resources that won’t be released right away if you don’t. You release resources by calling the Dispose() method, as shown here:

g.Dispose();

image.Dispose();

Figure 30-3 shows the completed web page created by this code.

C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

1019

Figure 30-3. A graphical label

Image Format and Quality

When you save the image, you can also choose the format you want to use. JPEG offers the best color support and graphics, although it uses compression that can lose detail and make text look fuzzy. GIF is often a better choice for graphics containing text, but it doesn’t offer good support for color. In .NET, every GIF uses a fixed palette with 256 generic colors. If you use a color that doesn’t map to one of these presets, the color will be dithered, leading to a less-than-optimal graphic.

Tip Another choice is the PNG format, which gives you the best of both the JPEG and GIF formats. However, the PNG format doesn’t work directly in a web page—instead, you need to wrap it in an <img> tag. Later in the section “Embedding Dynamic Graphics in a Web Page,” you’ll see how to take this step.

Quality isn’t just determined by the image format. It also depends on the way you render the original bitmap. GDI+ allows you to choose between optimizing your drawing code for appearance or speed. When you choose to optimize for the best appearance, .NET uses extra rendering techniques such as antialiasing to improve the drawing.

Antialiasing smooths jagged edges in shapes and text. It works by adding shading at the border of an edge. For example, gray shading might be added to the edge of a black curve to make a corner look smoother. Technically, antialiasing blends a curve with its background. Figure 30-4 shows a close-up of an antialiased ellipse.

Figure 30-4. Antialiasing with an ellipse

1020 C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

To use smoothing in your applications, you set the SmoothingQuality property of the Graphics object. You can choose between None, HighSpeed (the default), AntiAlias, and HighQuality (which is similar to AntiAlias but uses other, slower optimizations that improve the display on LCD screens). The Graphics.SmoothingQuality property is one of the few stateful Graphics class members. This means you set it before you begin drawing, and it applies to any text or shapes

you draw in the rest of the paint session (until the Graphics object is disposed of).

g.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias;

Tip Antialiasing makes the most difference when you’re displaying curves. That means it will dramatically improve the appearance of ellipses, circles, and arcs, but it won’t make any difference with straight lines, squares, and rectangles.

You can also use antialiasing with fonts to soften jagged edges on text. You can set the Graphics.TextRenderingHint property to ensure optimized text. You can choose between SingleBitPerPixelGridFit (fastest performance and lowest quality), AntiAliasGridFit (better quality but slower performance), and ClearTypeGridFit (the best quality on an LCD display). Or you can use the SystemDefault value to apply whatever font-smoothing settings the user has configured. SystemDefault is the default setting, and the default system settings for most computers enable text antialiasing. Even if you don’t set this, your dynamically rendered text will probably be drawn in high quality. However, because you can’t necessarily control the system settings of the web server, it’s a good practice to specify this setting explicitly if you need to draw text in an image.

The Graphics Class

The majority of the GDI+ drawing smarts is concentrated in the Graphics class. The Graphics class also provides a slew of methods for drawing specific shapes, images, and text. Table 30-1 describes these methods, many of which are used in the examples in this chapter.

Table 30-1. Graphics Class Methods for Drawing

Method

Description

DrawArc()

Draws an arc representing a portion of an ellipse specified by a

 

pair of coordinates, a width, and a height

DrawBezier() and

Draws the infamous and attractive Bezier curve, which is

DrawBeziers()

defined by four control points

DrawClosedCurve()

Draws a curve and then closes it off by connecting the endpoints

DrawCurve()

Draws a curve (technically, a cardinal spline)

DrawEllipse()

Draws an ellipse defined by a bounding rectangle specified by a

 

pair of coordinates, a height, and a width

DrawIcon() and

Draws the icon represented by an Icon object and (optionally)

DrawIconUnstreched()

stretches it to fit a given rectangle

DrawImage and

Draws the image represented by an Image-derived object and

DrawImageUnscaled()

(optionally) stretches it to fit a given rectangle

DrawLine() and DrawLines()

Draws a line connecting the two points specified by coordinate

 

pairs

C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

1021

Method

Description

DrawPath()

Draws a GraphicsPath object, which can represent a

 

combination of curves and shapes

DrawPie()

Draws a “piece-of-pie” shape defined by an ellipse specified by a

 

coordinate pair, a width, a height, and two radial lines

DrawPolygon()

Draws a multisided polygon defined by an array of points

DrawRectangle() and

Draws an ordinary rectangle specified by a starting coordinate

DrawRectangles()

pair and width and height

DrawString()

Draws a string of text in a given font

FillClosedCurve()

Draws a curve, closes it off by connecting the endpoints, and

 

fills it

FillEllipse()

Fills the interior of an ellipse

FillPath()

Fills the shape represented by a GraphicsPath object

FillPie()

Fills the interior of a “piece-of-pie” shape

FillPolygon()

Fills the interior of a polygon

FillRectangle() and

Fills the interior of a rectangle

FillRectangles()

 

FillRegion()

Fills the interior of a Region object

 

 

The DrawXxx() methods draw outlines (for example, the edge around a rectangle). The FillXxx() methods paint solid regions (for example, the actual surface inside the borders of a rectangle). The only exception is the DrawString() method, which draws filled-in text using a font you specify, and DrawIcon() and DrawImage(), which copy bitmap images onto the drawing surface.

If you want to create a shape that has both an outline in one color and a fill in another color, you need to combine both a draw and a fill method. Here’s an example that first paints a white rectangle and then adds a green border around it:

g.FillRectangle(Brushes.White, 0, 0, 300, 50); g.DrawRectangle(Pens.Green, 0, 0, 299, 49);

Note If you specify coordinates that are not in the drawing area, you won’t receive an exception. However, the content you draw that’s off the edge won’t appear in the final image. In some cases, this means a partial shape may appear (which might be exactly the effect you want).

You’ll notice that when you use a fill method, you need to specify a Brush object. When you use a draw method, you need to specify a Pen object. In this example, the code uses a prebuilt Pen and Brush object, which can be retrieved from the Pens and Brushes classes, respectively. Brushes retrieved in this way always correspond to solid colors. Pens retrieved in this way are always 1 pixel wide. Later in this chapter (in the “Pens” and “Brushes” sections), you’ll learn how to create your own custom pens and brushes for more exotic patterns.

Using the techniques you’ve learned, it’s easy to create a simple web page that draws a more complex GDI+ image. The next example uses the Graphics class to draw an ellipse, a text message, and an image from a file.

1022 C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

Here’s the code you’ll need:

protected void Page_Load(Object sender, EventArgs e)

{

//Create the in-memory bitmap where you will draw the image.

//This bitmap is 450 pixels wide and 100 pixels high. Bitmap image = new Bitmap(450, 100);

Graphics g = Graphics.FromImage(image);

//Ensure high-quality curves.

g.SmoothingMode = SmoothingMode.AntiAlias;

//Paint the background. g.FillRectangle(Brushes.White, 0, 0, 450, 100);

//Add an ellipse. g.FillEllipse(Brushes.PaleGoldenrod, 120, 13, 300, 50); g.DrawEllipse(Pens.Green, 120, 13, 299, 49);

//Draw some text using a fancy font.

Font font = new Font("Harrington", 20, FontStyle.Bold); g.DrawString("Oranges are tasty!", font, Brushes.DarkOrange, 150, 20);

//Add a graphic from a file. System.Drawing.Image orangeImage =

System.Drawing.Image.FromFile(Server.MapPath("oranges.gif")); g.DrawImageUnscaled(orangeImage, 0, 0);

//Render the image to the output stream. image.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);

//Clean up.

g.Dispose();

image.Dispose();

}

Figure 30-5 shows the resulting web page.

Figure 30-5. Using multiple elements in a drawing

C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

1023

Using a GraphicsPath

Two interesting methods that you haven’t seen yet include DrawPath() and FillPath(), which work with the GraphicsPath class in the System.Drawing.Drawing2D namespace.

The GraphicsPath class encapsulates a series of connected lines, curves, and text. To build a GraphicsPath object, you simply create a new instance and use the methods in Table 30-2 to add all the required elements.

GraphicsPath path = new GraphicsPath(); path.AddEllipse(0, 0, 100, 50); path.AddRectangle(New Rectangle(100, 50, 100, 50);

Table 30-2. GraphicsPath Methods

Method

Description

AddArc()

Draws an arc representing a portion of an ellipse specified

 

by a pair of coordinates, a width, and a height.

AddBezier() and AddBeziers()

Draws the infamous and attractive Bezier curve, which is

 

defined by four control points.

AddClosedCurve()

Draws a curve and then closes it off by connecting the

 

endpoints.

AddCurve()

Draws a curve (technically, a cardinal spline).

AddEllipse()

Draws an ellipse defined by a bounding rectangle specified

 

by a pair of coordinates, a height, and a width.

AddLine() and AddLines()

Draws a line connecting the two points specified by

 

coordinate pairs.

AddPath()

Adds another GraphicsPath object to this GraphicsPath

 

object.

AddPie()

Draws a “piece-of-pie” shape defined by an ellipse

 

specified by a coordinate pair, a width, a height, and

 

two radial lines.

AddPolygon()

Draws a multisided polygon defined by an array of points.

AddRectangle() and AddRectangles()

Draws an ordinary rectangle specified by a starting

 

coordinate pair and width and height.

AddString()

Draws a string of text in a given font.

StartFigure() and CloseFigure()

StartFigure() defines the start of a new closed figure. When

 

you use CloseFigure(), the starting point will be joined to

 

the endpoint by an additional line.

Transform(), Warp(), and Widen()

Applies a matrix transform, a warp transform (defined

 

by a rectangle and parallelogram), or an expansion,

 

respectively.

 

 

Optionally, you can also create a solid, filled figure from separate line segments. To do this, you first call the StartFigure() method. Then you add the required curves and lines using the appropriate methods. When finished, you call the CloseFigure() method to close off the shape by drawing a line from the endpoint to the starting point. You can use these methods multiple times to add several closed figures to a single GraphicsPath object. Here’s an example that draws a single figure based on an arc and a line:

1024 C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

GraphicsPath path = new GraphicsPath(); path.StartFigure();

path.AddArc(10, 10, 100, 100, 20, 50); path.AddLine(20, 100, 70, 230); path.CloseFigure();

Pens

When you use the DrawXxx() methods from the Graphics class, the border of the shape or curve is drawn with the Pen object you supply. You can retrieve a standard pen using one of the static properties from the System.Drawing.Pens class. These pens all have a width of 1 pixel. They differ only in their color.

Pen myPen = Pens.Black;

You can also create a Pen object on your own and configure all the properties described in Table 30-3. Here’s an example:

Pen myPen = new Pen(Color.Red); myPen.DashCap = DashCap.Triangle; myPen.DashStyle = DashDotDot; g.DrawLine(myPen, 0, 0, 10, 0);

Table 30-3. Pen Members

Member

Description

DashPattern

Defines a dash style for broken lines using an array of dashes and

 

spaces.

DashStyle

Defines a dash style for broken lines using the DashStyle enumeration.

LineJoin

Defines how overlapping lines in a shape will be joined.

PenType

The type of fill that will be used for the line. Typically this will be

 

SolidColor, but you can also use a gradient, bitmap texture, or hatch

 

pattern by supplying a brush object when you create the pen. You

 

cannot set the PenType through this property, however, because it is

 

read-only.

StartCap and EndCap

Determines how the beginning and ends of lines will be rendered.

 

You can also define a custom line cap by creating a CustomLineCap

 

object (typically by using a GraphicsPath) and then assigning it to the

 

CustomStartCap or CustomEndCap property.

Width

The pixel width of lines drawn by this pen.

 

 

The easiest way to understand the different LineCap and DashStyle properties is to create a simple test page that loops through all the options and displays a short line segment of each. The following web-page code creates a drawing that does exactly that:

protected void Page_Load(object sender, System.EventArgs e)

{

//Create the in-memory bitmap where you will draw the image.

//This bitmap is 300 pixels wide and 50 pixels high.

Bitmap image = new Bitmap(500, 400);

Graphics g = Graphics.FromImage(image);

C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

1025

//Paint the background. g.FillRectangle(Brushes.White, 0, 0, 500, 400);

//Create a pen to use for all the examples. Pen myPen = new Pen(Color.Blue, 10);

//The y variable tracks the current y (up/down) position

//in the image.

int y = 60;

//Draw an example of each LineCap style in the first column (left). g.DrawString("LineCap Choices", new Font("Tahoma", 15, FontStyle.Bold),

Brushes.Blue, 0, 10);

foreach (LineCap cap in System.Enum.GetValues(typeof(LineCap)))

{

myPen.StartCap = cap; myPen.EndCap = cap; g.DrawLine(myPen, 20, y, 100, y);

g.DrawString(cap.ToString(), new Font("Tahoma", 8), Brushes.Black, 120, y - 10);

y += 30;

}

//Draw an example of each DashStyle in the second column (right).

y = 60;

g.DrawString("DashStyle Choices", new Font("Tahoma", 15, FontStyle.Bold), Brushes.Blue, 200, 10);

foreach (DashStyle dash in System.Enum.GetValues(typeof(DashStyle)))

{

//Configure the pen. myPen.DashStyle = dash;

//Draw a short line segment. g.DrawLine(myPen, 220, y, 300, y);

//Add a text label.

g.DrawString(dash.ToString(), new Font("Tahoma", 8), Brushes.Black, 320, y - 10);

// Move down one line. y += 30;

}

// Render the image to the output stream. image.Save(Response.OutputStream,

System.Drawing.Imaging.ImageFormat.Gif);

g.Dispose();

image.Dispose();

}

Figure 30-6 shows the resulting web page.

1026 C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

Figure 30-6. Different pen options

Brushes

Brushes are used to fill the space between lines. Brushes are used when drawing text or when using any of the FillXxx() methods of the Graphics class for painting the inside of a shape.

You can quickly retrieve a predefined solid brush using a static property from the Brushes class, as shown here:

Brush myBrush = Brushes.White;

You can also create a custom brush. You need to decide what type of brush you are creating. Solid brushes are created from the SolidBrush class, and other classes allow fancier options.

HatchBrush: A HatchBrush has a foreground color, a background color, and a hatch style that determines how these colors are combined. Typically, colors are interspersed using stripes, grids, or dots, but you can even select unusual pattern styles such as bricks, confetti, weave, and shingles.

LinearGradientBrush: The LinearGradientBrush allows you to blend two colors in a gradient pattern. You can choose any two colors (as with the hatch brush) and then choose to blend horizontally (from left to right), vertically (from top to bottom), diagonally (from the top-left corner to the bottom-right corner), or diagonally backward (from the top-right corner to the bottom-left corner). You can also specify the origin point for either side of the gradient.

TextureBrush: The TextureBrush attaches a bitmap to a brush. The image is tiled in the painted portion of the brush, whether it is text or a simple rectangle.

C H A P T E R 3 0 DY N A M I C G R A P H I C S A N D G D I +

1027

You can experiment with all these brush types in your applications. Here’s an example of the drawing logic you need to test all the styles of LinearGradientBrush:

protected void Page_Load(object sender, System.EventArgs e)

{

//Create the in-memory bitmap. Bitmap image = new Bitmap(300, 300);

Graphics g = Graphics.FromImage(image);

//Paint the background. g.FillRectangle(Brushes.White, 0, 0, 300, 300);

//Show a rectangle with each type of gradient. LinearGradientBrush myBrush;

int y = 20;

foreach (LinearGradientMode gradientStyle in System.Enum.GetValues(typeof(LinearGradientMode)))

{

//Configure the brush.

myBrush = new LinearGradientBrush(new Rectangle(20, y, 100, 60), Color.Violet, Color.White, gradientStyle);

//Draw a small rectangle and add a text label. g.FillRectangle(myBrush, 20, y, 100, 60); g.DrawString(gradientStyle.ToString(), new Font("Tahoma", 8),

Brushes.Black, 130, y + 20);

//Move to the next line.

y += 70;

}

// Render the image to the output stream. image.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);

g.Dispose();

image.Dispose();

}

Figure 30-7 shows the result.

Tip You can also create a pen that draws using the fill style of a brush. This allows you to draw lines that are filled with gradients and textures. To do so, begin by creating the appropriate brush and then create a new pen. One of the overloaded pen constructor methods accepts a reference to a brush—that’s the one you need to use for a brush-based pen.