GRAphical Programming for Eiffel index contents

Chapter VI. Painting and Drawing

GRAPHICS_CONTEXT
   DISPLAY_GRAPHICS_CONTEXT
   PRINTER_GRAPHICS_CONTEXT
   MEMORY_GRAPHICS_CONTEXT
GDI_RESOURCE
   PEN
   BRUSH
   FONT
COLOR

When your applications want to paint some images, graphic figures or print out some text information then according to the Windows Graphic Device Interface (GDI) ideology it should use Graphics Context. Graphics Context (GC) is an abstract "paper sheet" corresponding to a window area in your program. Also you have drawing tools: pens, brushes and fonts. All you need is to select a desired tool and using it draw on GC.

First of all we must introduce class COLOR.


COLOR

By default Windows defines a color as a combination of three primary colors - red, green, and blue. Windows identifies a color by giving values that identify how much red, green, and blue the color has. Such values are known as RGB values (red, green, blue) and consist of three integer values specifying the intensities of the three components. Range of each value is from 0 to 255. Black has the minimum intensity for red, green, and blue, so the RGB value for black is (0,0,0). White has the maximum intensity for red, green, and blue, so its value is (255, 255, 255). The following code shows how to create a color object in GRAPE. It's simple - define a color object and use its creation procedure to specify its RGB-color, later, during the object life, you also can change this color - simply use the creation feature again.

red, dark_green, yellow, brown : color
...
-- call colors creation procedure 
-- with red component value 255
!!red.make_rgb (255,0,0)
-- now make dark green color
-- with green component value 128
!!dark_green.make_rgb (0,128,0) 
-- Now a bit difficult - yellow color
-- yellow is red plus green
!!yellow.make_rgb (255,255,0) 
-- and brown is dark red plus dark green
!!brown.make_rgb (128,128,0) 
...

You can use the powerful RGB color mechanism to specify about 16 millions of colors (actually the number of available pure colors depends on the current video-mode of your videocard) but of course with GRAPE you do not need to take in mind that, e.g. purple is RGB(128,0,128). You can simple use proper creation procedure - !!c.make_purple(). All basic colors have the same names for the creation procedures.

Also you can specify the system color as a color of you objects. The system color is a color of control elements in Windows such as colors of button's background, window titles, etc. For more information see Appendix A.


GDI resources

Next we give a brief GDI resource tour. For detailed information please see our On-Line Help Reference. In GRAPE there are three basic resources Pen, Brush and Font.


PEN

A pen is a graphics tool used to draw lines and curves. Drawing applications use pens to draw freehand lines, straight lines, and curves. There are three pen attributes that define the type of the pen and its characteristics: width, style, color. It is specified in the pen creation procedure.

pen.make ( style : INTEGER, weight : INTEGER, pencolor : COLOR )

The first parameter determines the pen style. GRAPE provides the following pen styles (it corresponds to the styles in the Windows GDI). Here are gray rectangles framed with different style's borders.


Figure 6.1 Pen styles.

NULL_PEN - specifies the pen used with filled painting features when you want to paint figures without border.

The second parameter determines the pen weight. Weight is specified in pixels (unit corresponding to a dot on the screen). You can specify any reasonable positive integer value. GRAPE class PEN provides two integer constants NORMAL_WIDTH and THICK_WIDTH setting the pen pixel's weight 1 and 3 respectively. In Appendix C we describe how to define the pen weight in device independent units (e.g. in millimeters)

The third parameter determines the pen's color.

Here is a simple example defining and creating two pens (red and black are color objects defined somewhere else):

red_pen, black_dot_pen : PEN;
...
!!red_pen.make (red_pen.SOLID_PEN, black_pen.NORMAL_WIDTH, red );
!!black_dot_pen.make (black_dot_pen.DOT_PEN, 5, black );
...

BRUSH

Brush is a graphics tool to paint the interior of shapes (polygons, ellipses, etc.).

There are three types of logical brushes (solid, hatch, and pattern) as it's shown at the following illustration.


Figure 6.2 Brush styles.

The brush style specified by the creation procedure:

!!p1.make_solid ( Blue );
!!p2.make_hatched ( p2.DIAGONAL_HATCH, Red );
!!p3.make_pattern ( pattern_picture );

FONT

Class FONT provides basic tools for operating Windows fonts. Fonts are used to drawing text with different styles. In Windows, a font is a collection of characters and symbols sharing a common design. To draw something with a particular font you should select it into the graphics context.

There are two creation procedures make and make_default. The first feature make creates a font with the default parameters. Normally it is the standard platform's font. To create a specific font object a programmer should set the appropriate font attributes after calling these creation features. Fortunately you can use make_default's fonts if you too lazy to specify the particular fonts' attributes. The latter feature creates one of the standard platform's fonts (normal or small).


Font attributes

Character Size

In Windows, the size of a font is an imprecise value. It can generally be determined by measuring the distance from the bottom of a lowercase "g" to the top of an adjacent uppercase "M," as shown in the following illustration.

A font's size is specified in units called points. A point is 0.013837 of an inch. Following the point system devised by Pierre Simon Fournier, it is common practice to approximate a point as 1/72 inch.

Class FONT contains integer attribute height that keeps the font's size and following feature is used to set the desired height of the font.

set_height ( h : INTEGER )

Note Character width is calculated automatically when you specify a new character height. You can obtain it by calling get_font_average_char_width() and get_font_max_char_width() for GRAPHICS_CONTEXT objects. Please refer to our On-Line Reference or wait a bit till you finds it later in this chapter.


Font Style

The term style refers to the weight and slant of a font.

Class FONT provides a set of features that trigger available styles for the current font object.

set_bold ( val : BOOLEAN )
set_italic ( val : BOOLEAN )
set_underline ( val : BOOLEAN )
set_strike_out ( val : BOOLEAN )

When all the styles are "turned off" we have default normal font style:

    Normal
set_bold ( true ) Bold
set_italic ( true ) Italic
set_underline ( true ) Underline
set_strike_out ( true ) Strikeout

For example, the following code combines Underline Bold Italic Font:

fnt : FONT;
...
!!f.make ();
fnt.set_bold ( true );
fnt.set_italic ( true );
fnt.set_underline ( true );

Font Family

Windows organizes fonts by their family; a family is a set of fonts having the same stroke width and serif characteristics. Windows categorizes families with five family names. The second name DONTCARE allows an application to use the default font.

The following table describes the names of the font families.

Font-family name Description
DECORATIVE Specifies a novelty font. An example is Old English.
DONTCARE Specifies a generic family name. This name is used when information about a font does not exist or does not matter.
MODERN Specifies a monospace font with or without serifs. Monospace fonts are usually modern; examples include Pica, Elite, and Courier New®.
ROMAN Specifies a proportional font with serifs e.g. Times New Roman
SCRIPT Specifies a font that is designed to look like handwriting;
examples include Script and Cursive.
SWISS Specifies a proportional font without serifs. An example is Arial.

To specify font family class FONT provides feature set_family that takes as a parameter one of the family's constants from the table above. For example:

fnt.set_family ( ROMAN )

Face name

You can also directly specify a font's typeface name. For example "Arial" or "MS Sans Serif."

Use feature set_face_name:

fnt.set_face_name ( "Arial" );

Color

To specify the font color use set_color feature.

!!c.make_blue;
fnt.set_color ( c );

Escapement

Also as one of the font's attributes you can specify the angle between X-axis and the font base line. Feature set_escapement takes angles in 0.1 degree units.

fnt.set_face_name ( "Arial" );
fnt.set_escapement ( 450 );

Note Escapement is available not for all fonts. Usually it's available when the selected font is a TrueType® font.


For complete example how different font attributes affect their visual presentations see FONTVIEW samples from GRAPE's EXAMPLES directory.


Figure 6.3 The FONTDEMO example.


GRAPHICS_CONTEXT

Now it's time to introduce the main topic of the chapter GRAPHICS_CONTEXT (GC). This class is deferred and provides the basic device-independent painting features. In this chapter we also consider all the features of the class.


Wrap painting

Before painting or setting anything with using GC you must initialize a graphics device context by establishing a link to the target output device. There are two features begin_paint and end_paint that perform this low level service. Every request to the graphics engine is valid only if it occurs between the calls of the begin_paint and end_paint feature. Only calls affecting the state of the graphics context are allowed outside these feature calls.

dgc.begin_paint;
   ... painting on dgc ...
dgc.end_paint;

Selecting painting tools

Now return to the resource tools discussed at the beginning of this chapter. To paint using your own resource object you must select it. GC has four features to do it - one universal and three explicit ones:

select_resource ( resource : GDI_RESOURCE ) : GDI_RESOURCE
select_pen ( a_pen : PEN )
select_brush ( a_brush : BRUSH )
select_font ( a_font : FONT )

Graphics context has its own resource tools selected by default (e.g. the normal width black pen, solid white brush, system fixed font ) used when you don't specify your own ones.


Paint and draw

There are two groups of painting features draw_xxx and paint_xxx. The first group draws outline primitives using the selected pen object. The second group paints filled primitives using the selected pen and brush.

Here we consider only the small amount of primitives available in class GRAPHICS_CONTEXT.

For the complete list please refer to our On-Line Help Reference.

draw_point ( x, y : REAL, a_color : COLOR )

Draw a point using given color.

draw_line ( x1, y1, x2, y2 : REAL)

Draw a line using the current pen. It's the fast equivalent to the following pair move_to (x1, y1) and line_to (x2, y2).

draw_rectangle ( x1, y1, x2, y2 : REAL )
paint_rectangle ( x1, y1, x2, y2 : REAL )

Draw a rectangle using the current pen. In addition to drawing the second feature fills the rectangle by the current brush.

draw_circle ( xc, yc, radius : REAL )
paint_circle ( xc, yc, radius : REAL )

Draw a circle using the current pen. In addition to drawing the second feature fills the circle by the current brush.

Now we can present an example demonstrating all discussed above. This is only a piece of the code: the on_paint feature. The full code you can find in the \EXAMPLES\DRAW\DEMO1.E file.

on_paint : INTEGER is
   -- paint red square with black frame
local
   dgc : DISPLAY_GRAPHICS_CONTEXT;
   blackpen : PEN;
   redbrush : BRUSH;
   c : color;
do
   !! c.make_red;
   !!redbrush.make_solid (c);
   !!c.make_black;
   !!blackpen.make( blackpen.SOLID_PEN, 1, c );
   !!dgc.make_with_assignment ( Current ); -- window object
   dgc.begin_paint;
   dgc.select_pen (blackpen);
   dgc.select_brush (redbrush);
   dgc.paint_rectangle (10,10,90,90);
   dgc.end_paint;
   Result := processed;
end; -- on_paint 

Display Graphics Context

You perhaps note in the example above that we introduced a new class DISPLAY_GRAPHICS_CONTEXT (DGC). When you are outputting your drawing on a computer's display there are a lot of other Windows applications which may share the entire screen with you. One of the principals is that each application must use its own window (or windows) for information output. In GRAPE it means that you must assign a target tile object to a display device context.

There are two way of assigning target TILE to the DISPLAY_GRAPHICS_ CONTEXT. To use the creation procedure make_with_assignment or to call feature assign_target_tile before any operation on DGC. You can assign the same tile for several DGC objects.


When painting

Usually an application draws something during the on_paint callback feature (see the chapter dedicated to callbacks). Fortunately you may draw not only inside on_paint but always keep in mind one important restriction - the target tile must be initialized and now you should understand why painting inside the features of TILE such as make, on_create raise exception "tile wasn't initialized".


Printing text

There is a list of features used for printing a text.

put_character ( c : CHARACTER, x, y : REAL )

Output a single character string at the specified position using the font currently selected.

put_string ( s : STRING, x, y : REAL )

Output a character string at the specified position using the font selected.

put_string_clipping ( s : STRING, tx, ty, x, y, w, h : REAL )

Output a character string at the specified position clipping the string to the specified rectangular area.

fill_with_string ( s : STRING, x, y, width : REAL )

Fill the specified width with the line of some text. Character spacing will be adjusted automatically to the width specified.

put_text ( s : STRING; x, y : REAL)

Put some text. The output can occupy several lines.

put_text_cliping ( s : STRING; x, y, w, h : REAL; ha, va : INTEGER )

Put lines of some text into a clipping rectangle with the specified alignment.



Text alignment options

You can align any output text vertically and horizontally using the following alignment features.

set_horizontal_text_alignment ( hcode : INTEGER )
get_horizontal_text_alignment : INTEGER

Where hcode is one of the following:

TA_LEFT Aligns the drawing point with the left side of the bounding rectangle. This is a default setting
TA_CENTER Aligns the drawing point with the horizontal center of the bounding rectangle
TA_RIGHT Aligns the drawing point with the right side of the bounding rectangle.
set_vertical_text_alignment ( vcode : INTEGER )
get_vertical_text_alignment : INTEGER

Where vcode is one of the following:

TA_TOP Aligns the drawing point with the top of the bounding rectangle. This is a default setting.
TA_BASELINE Aligns the drawing point with the baseline of chosen font.
TA_BOTTOM Aligns the drawing point with the bottom of the bounding rectangle.

Current font info and text measurement

There are a lot of features provided by class DISPLAY_GRAPHICS_CONTEXT that are used to obtain different font characteristics. Here we include only a list of them. For more information on each feature please look at our On-Line Help Reference.

get_font_face_name : STRING
get_font_cell_height : REAL
get_font_ascent : REAL
get_font_descent : REAL
get_font_external_leading : REAL
get_font_average_char_width : REAL
get_font_max_char_width : REAL
get_font_default_char : INTEGER
get_font_break_char : INTEGER
get_intercharacter_spacing : REAL
measure_char_width ( char : CHARACTER ) : REAL
measure_text_width ( s : STRING ) : REAL
measure_text_height ( s : STRING ) : REAL


Drawing mode

The drawing mode defines how foreground colors are being mixed with the existing screen colors for pen, brush, picture and text objects. There are two features dealt with the drawing mode: get_draw_mode and set_draw_mode. They are simple. In GRAPE there are sixteen different modes shown in following table.

Mode

Effect
src is object's color; dest is tile's color

ROP_CLEAR dest := always black color
ROP_AND dest := src AND dest
ROP_AND_REVERSE dest := src AND ( NOT dest)
ROP_COPY dest := src (* This is default mode)
ROP_AND_INVERTED dest := (NOT src) AND dest
ROP_NOOP dest := dest (* painting "ignored")
ROP_XOR dest := src XOR dest
ROP_OR dest := src OR dest
ROP_NOR dest := (NOT src) AND (NOT dest)
ROP_EQUIV dest := (NOT src) XOR dest
ROP_INVERT dest := NOT dest
ROP_OR_REVERSE dest := src OR (NOT dest)
ROP_COPY_INVERTED dest := NOT src
ROP_OR_INVERTED dest := (NOT src) OR dest
ROP_NAND dest := (NOT src) OR (NOT dest)
ROP_SET dest := always white color

Next we include an example how this mode affects painting. This is only a piece of the code - the on_paint feature. You can find the complete example in the \EXAMPLES\DRAW\DEMO2.E file.

on_paint : INTEGER is
   local i : INTEGER;
do
   dgc.begin_paint;
   from  i := dgc.ROP_CLEAR
   until i > dgc.ROP_SET
   loop
     dgc.set_draw_mode (i);
     dgc.select_brush (greenbrush);
     dgc.paint_rectangle ( 0, i*20, 35, i*20+14);
     dgc.select_brush (redbrush);
     dgc.paint_rectangle ( 5, i*25, 40, i*20+19);
     i := i + 1;
   end -- loop
   Result := processed;
end;

Background mode

The background mode defines how background colors are being mixed with the existing screen colors for picture and text operations. There are two features dealt with the background mode: get_background_mode and set_background_ mode.

Mode Effect
OPAQUE_BACKGROUND Background is filled with the current background color before the text, hatched brush, or pen is drawn.
TRANSPARENT_BACKGROUND Background remains untouched

For example, we set the window background to the gray color. Suppose that this code is executing for a window object:

!!c.make_gray;
set_image_background ( c );

Now if you draw any text it will clip white rectangles - each character erases background around itself. By default the OPAQUE_BACKGROUND mode is selected.

dgc.draw_text ( "aBcD", 10, 10 );

To draw a text with the untouched background set the TRANSPARENT_ BACKGROUND mode.

dgc.set_background_mode (
dgc.TRANSPARENT_BACKGROUND );
dgc.draw_text ( "aBcD", 10, 30 );

Painting PICTURE

GRAPE has a special class called PICTURE that represents a bitmap picture object. There are three possibilities:

There is a special feature in class CRAPHICS_CONTEXT to display a picture object on GC.

put_picture ( p : PICTURE, x, y : REAL )

For example the following code displays bitmap file CLOUDS.BMP .

p : PICTURE
...
!!p.make ("CLOUDS.BMP");
...
dgc.begin_paint;
dgc.put_picture (p,0,0);
...

Painting ICON

GRAPE has special class ICON that represents an icon object. There are two possibilities:

There is a special feature in class DISPLAY_CRAPHICS_ CONTEXT to paint a icon object on DGC.

put_icon ( ico : ICON, x, y : REAL )

The following code displays one of the predefined icons.

m_icon : ICON
...
!!m_icon.make ( m_icon.OPENED_BOOK_ICON );
...
dgc.begin_paint;
dgc.put_icon ( m_icon, 10, 10 );
...

Saving GC state

Suppose you have a painting feature that changes the current pen and brush for GC or sets some exotic drawing mode, etc. Before this feature has been finished you need to restore the state of GC - otherwise in the other features painting on the same CG you need explicitly select the painting resources before painting anything. There is another simple way how to do it. You can save the state of GC, perform any operations on it, and restore it to the original state. Class GRAPHICS_CONTEXT provides two procedures save_gc and restore_gc that perform this job for you.

dgc.save_gc
...
dgc.set_draw_mode ()
-- some other operations
...
dgc.restore_gc

When you are saving GC its contents is pushed into a small stack thus, pairs save_gc ... restore_gc can be nested. Don't forget to pop the elements from this stack - beware of possible stack overflowing.


© Object Tools -- info@object-tools.com -- December 1999