Loading…
Text content (OCR)
THE MAGIC OF
MACINTOSH
Programming Graphics
and Sound
William B. Twitty
CDK Didmon,
Ch. ©-F. Cee lhata
Ee spleen
i“
ars
ovl'h 950
THE MAGIC OF
MACINTOSH
ONT
THE MAGIC OF
MACINTOSH
Programming Graphics
and Sound
William B. Twitty
Scott, Foresman and Company
Glenview, Illinois London
A
A
For Othermamma
Macintosh is a trademark of McIntosh Laboratory, Inc. and is licensed to Apple
Computer, Inc. and is being used with express permission of its owner.
MacPaint, MacWrite, MacDraw, and Macintosh Pascal are trademarks of Apple
Computer, Inc.
Library of Congress Cataloging-in-Publication Data
Twitty, William B.
The magic of Macintosh.
Bibliography: p. 305
Includes index.
1. Macintosh Computer)—Programming. 2. Computer
graphics. 3. Computer sound processing. I. Title.
QA76.8.M3T84 1986 006.6’76 86-3750
ISBN 0-673-18253-3
1 2 3 4 5 6—KPF—91 90 89 88 87 86
ISBN 0-673-18253-3
Copyright © 1986 William B. Twitty and Pacific Systems, Inc.
All Rights Reserved.
Printed in the United States of America.
Notice of Liability
The information in this book is distributed on an “‘As Is’”’ basis, without
warranty. Neither the author nor Scott, Foresman and Company shall have any
liability to customer or any other person or entity with respect to any liability,
loss, or damage caused or alleged to be caused directly or indirectly by the
programs contained herein. This includes, but is not limited to, interruption of
service, loss of data, loss of business or anticipatory profits, or consequential
damages from the use of the programs.
Scott, Foresman Professional Publishing Group books are available for bulk
sales at quantity discounts. For information, please contact Marketing Manager,
Professional Books, Professional Publishing Group, Scott, Foresman and
Company, 1900 East Lake Avenue, Glenview, IL 60025.
AN
PREFACE
The magic of the Macintosh is its graphics and sound. The Macintosh’s
ease of use and its appeal to nonprogrammers depend on its graphics.
Anyone who writes programs for the Macintosh quickly finds that learning
how to generate and manipulate images is an absolute must; you can’t do
anything on the Macintosh until you can understand and use the Quick-
Draw graphics package.
This book was written to introduce programmers with little or no
graphics background to Macintosh graphics and sound. The book has a
fair amount of technical material, but there’s also a lot of fun with graphics
and sound.
The early chapters are very basic and deal with graphics fundamen-
tals, QuickDraw, making images on the Macintosh, and drawing text in
various type fonts. The later chapters address more technical subjects but
always with the understanding that the reader may be able to program but
has no prior experience with computer graphics.
The material is ordered so that a user can start writing programs
immediately without having to understand all of the details of QuickDraw
coordinates and mathematics. Those kinds of details are covered in later
chapters.
The emphasis is on explaining by example. I felt it important to
provide a concise example for each concept that is explained in the text,
so The Magic of Macintosh is filled with programming examples and
illustrations. The examples are designed so that the reader can take parts
of the programs and transfer them to his or her own applications. They are
a set of pretested parts for the software builder.
The example programs are written in Macintosh Pascal. To keep them
as simple as possible, I did not use any toolbox routines except those
directly associated with graphics and sound.
Chapter 1 introduces the reader to computer graphics and their
implementation on the Macintosh. Chapter 2 explains the fundamentals of
drawing in two dimensions and coordinate systems and begins introduc-
ing QuickDraw topics. Chapter 3 goes into more detail on drawing shapes
and patterns with QuickDraw. Chapter 4 introduces the reader to text
fonts and how they are drawn on the Macintosh with tools from the
QuickDraw package. Chapter 5 deals with the mouse, the cursor, and
more advanced tools in QuickDraw, as well as pictures, polygons, and
regions. Chapter 6 leads the reader into the more technical subjects
through an understanding of the fundamental concepts behind Quick-
Draw, its coordinate systems and data structures.
From chapter 7 on, the topics are not strictly limited to the Macintosh
and QuickDraw but range over a variety of technical methods used in
computer-aided design systems and other graphics programs. Chapter 7
discusses how to draw and store objects. Chapter 8 takes us into the exotic
climes of spline curves and fractals.
Chapter 9 brings the book to a close with explanations and examples
of how to produce complex sounds on the Macintosh.
Writing a technical book is a demanding task, but along with the work
on this book, there was a lot of fun doing graphics and sound with the
magical Macintosh. I hope that you have as much fun with this book as
I did.
Programmers new to the Macintosh will find the first book in this
series, Programming the Macintosh: An Advanced Guide, to be another
useful addition to their library.
Many people helped to create and produce this book. Among them
are the folks at Apple Computer who provided information and technical
advice and my editor at Scott, Foresman, Richard Swadley, who had more
patience than one could reasonably expect. A special thank-you goes to
Jeanine Johnson, who put in many editing hours helping to make sense out
of my technical ramblings and fumble-fingered typing.
eT
CONTENTS
cHapteR 1 UNLEASHING THE MAGICIAN 1
The Magic in Your Macintosh 2
The Macintosh Display 3
Pixel Coordinates 4
Jaggies 7
Halftone Images 8
QuickDraw 9
Programming 9
cHarteR 2 DRAWING IN TWO DIMENSIONS ‘11
Dimensions 12
Coordinate Systems 12
Pixels and Memory 13
The Pen 13
Coordinate Transformations 15
Points and Rectangles 20
Clipping and Windows 24
cHapteR 3 DRAWING SHAPES AND PATTERNS 27
Coordinates and the Pen 28
The Pen Pattern 29
Drawing Modes 31
QuickDraw Shapes 40
Drawing QuickDraw Shapes 44
cHapten 4 DRAWING TEXT 49
Displaying Text with Type Fonts 50
Type Style 51
Type Size 51
Font Files 53
Text Character Images 53
Kerning 55
QuickDraw and the Font Manager 57
QuickDraw Routines 58
Drawing Text 59
Getting Information about the Font 61
A Program to Draw a Font’s Character Set 63
cHapten 5 MORE TOOLS FOR THE MAGICIAN 69
The Cursor 70
The Mouse 76
Pictures, Polygons, and Regions 79
Creating QuickDraw Pictures 83
QuickDraw Polygons 87
Using Regions 94
cHapteR 6 QUICKDRAW COORDINATES AND
DATASTRUCTURES 101
Coordinates and Data Structures 102
QuickDraw Coordinates 102
Pixels and Memory 106
The Graph Port 108
More on Coordinates 111
Translation and Scaling 116
cHapteR 7 DRAWING OBJECTS 119
What’s an Object? 120
Using Data Structures to Define Objects 121
An Object as a Collection of Shapes 122
Basic Trigonometry 128
Rotation 130
Rotation about an Arbitrary Point 134
Scaling an Object 135
A Program with Objects 139
Modifying the Object-Drawing Program 152
CHAPTER 8
CHAPTER 9
SPLINES AND FRACTALS 179
Drawing Smooth Curves 180
Spline Curves 184
The Spline Program 190
Drawing Jagged Curves 191
Drawing Fractals 193
Simulating Nature 200
SOUND MAGIC 227
Sound Basics 228
Making Music with the Macintosh 230
SysBeep and Note 232
Controlling the Volume 233
The Sound Synthesizers 234
Generating Square-Wave Tones 237
Sound from Free-Form Waves 239
Using the Four-Voice Synthesizer 245
APPENDIXES 265
A QuickDraw Data Structures 267
B_ QuickDraw Routines 271
C Mouse Routines 293
D
E
Macintosh Pascal Window Routines 295
Sound Routines and Data Structures 297
GLOSSARY 301
BIBLIOGRAPHY 305
INDEX 307
CAN
CHAPTER
“1 “UNLEASHING THE
MAGICIAN
The Magic in Your Macintosh
The Macintosh Display
Pixel Coordinates
Jaggies
Halftone Images
QuickDraw
Programming
eee ec
UNLEASHING THE MAGICIAN
THE MAGIC IN YOUR
MACINTOSH
A magician is someone who performs supernatural and astonishing feats
through the mastery of secret and mysterious forces. And what could be
more magical than a tan-colored box that draws pictures and makes music
by its own hand? Your Macintosh computer is like a magician, accom-
plishing tasks that no small computer could before. However, the real
magic within the Macintosh lies in the programs that produce the pictures
and sound.
Writing programs for sound and graphics may seem an arcane art
calling for secret knowledge, but taken step by step, it’s really very simple.
When you learn the basics of computer graphics with examples pro-
grammed for the Macintosh, you are on your way to unleashing the
magician in your Macintosh.
The Macintosh produces pictures of striking quality for an inexpen-
sive computer. The basis of this high quality is the high-resolution display,
which produces finely detailed images. The inexpensive high-resolution
graphics open up many new possibilities for graphics on personal
computers.
The high-resolution display provides the fine artist with an entirely
new medium in which to work and a new set of tools as well. Every
medium has its own characteristics, and artists have been quick to capi-
talize on the Macintosh’s. Commercial artists were not caught napping
either. Some of the first third-party products for the Macintosh were disks
containing pictures that any nonartist could use to add spice to proposals,
newsletters, circulars, or other types of documents produced on the
Macintosh.
Business people use the graphics capabilities of the Macintosh to
prepare for presentations and illustrate financial data. Engineers now have
a tool for quickly producing graphs and charts based on engineering
calculations. There are a number of low-cost computer-aided design
programs on the market that will do everything from lay out printed circuit
boards to help you design a garden. Of all the new application programs
for personal computers, the most exciting are the ones designed specially
for the Macintosh. They relate pictures to information in new and inno-
vative ways. Some are so new in concept that old labels no longer apply.
Commercial packages provide graphics-based tools for doing a job. In
this book we are more interested in the fun of graphics and sound on the
Macintosh. We don’t have any specific goals to produce a useful program.
‘We're in it for the fun. Along the way we will learn a lot about graphics and
NT
THE MACINTOSH DISPLAY 3
how to write programs that do useful work, but that’s more a side effect
than a goal.
Some of the more basic graphics software techniques that we explore
are useful for creating paint-type programs, sometimes called graphics
editors. These programs allow you to create pictures by drawing on the
Macintosh screen. You can manipulate the pictures only as if they were on
a piece of paper. The tools the program provides are similar to the
drawing, cutting, and pasting tools that artists use. You will also learn
about more advanced techniques that let you store a mathematical
description of an object. You can then manipulate the object as its picture
is displayed, moving it, changing the scale, or rotating it. These are the
kinds of techniques used in writing computer-aided design programs,
used for such things as architectural drawing or laying out printed cir-
cuit boards.
The remainder of this chapter covers some very basic information
about computer graphics. If you already understand bit-mapped displays,
pixels, aliasing, and halftone images, you should skip to the beginning of
the next chapter.
THE MACINTOSH DISPLAY
ON
The Macintosh has a high-resolution bit-mapped display. When you draw
a picture on the Macintosh, it creates the image by lighting small discrete
squares on the screen. If you draw a line, the Macintosh turns on each little
square that falls along the course of the line (figure 1.1). These small
squares are called pixels (a contraction of picture elements). A pixel is the
smallest portion of the screen that you can control. You can turn a pixel
on (make it black) or turn it off (make it white). All images that you create
Figure 1.1 A line on a bit-mapped display
UNLEASHING THE MAGICIAN
on the screen are made up of sets of pixels turned on or off. It sounds like
a crude way to create a picture, but if the pixels are small enough, they
blend together, and the picture appears more a continuous range of gray
colors than a collection of individual pixels.
Each pixel has an exact location on the screen and never moves. If
you turn on a pixel, it will remain on until you turn it off. A display that
is composed of pixels is called a bit-mapped display because each pixel
corresponds to 1 bit in the computer’s random access memory (RAM).
PIXEL COORDINATES
AUN
Since each pixel has a discrete, dedicated location on the display, it stands
to reason that we have a method of selecting exactly which pixel we will
turn on or off. You select a pixel by specifying the vertical and horizontal
coordinates of its position on the screen. The coordinate system is like the
Cartesian coordinate system that you learned about in high school algebra.
Each point has a vertical coordinate that specifies its location in the vertical
direction and a horizontal coordinate that specifies its location in the
horizontal direction.
Figure 1.2 shows a portion of the display (the upper left corner)
somewhat enlarged so that we can identify individual pixels. Note how the
horizontal and vertical coordinates uniquely identify a pixel.
By turning sets of pixels on and off, we can draw lines, circles, or
objects of any shape. Figure 1.3 shows a small square, a circle, and a text
H
o 123 4 5 6 7 8 9 10 11
°
Nau eon
Lome H=3, U=6
Figure 1.2 Pixels and coordinates
A
PIXEL COORDINATES 5
O a
Figure 1.3 Pixels and shapes
character. Each is shown enlarged so that you can see the individual pixels
and also at normal size so that you can see the blending effect.
When you draw a picture by setting pixels, you can either draw a
black image against a white background, as in figure 1.3, or do the
opposite, drawing a white image on a black background. Most Macintosh
software draws black images on a white background. It’s more like
drawing on a piece of paper. In the examples, I do it that way, too. When
I talk about setting a pixel or turning it on, I mean making it black.
When we write a program to draw a picture, the program must set
each individual pixel that makes up the picture, specifying the coordinates
of each pixel and whether the pixel is to be turned on or off. The range
of values for pixel locations depends on the shape of the screen and the
number of pixels. The Macintosh has a rectangular screen 512 pixels wide
and 342 pixels high. The horizontal and vertical coordinates of the pixel
in the upper left corner of the screen are both 0. The horizontal coordinate
of the pixel in the lower right corner is 511; the vertical coordinate is 341.
We usually write a pixel’s coordinates as a pair of numbers in
parentheses, the horizontal coordinate first. The coordinates of the upper
left corner of the screen are thus (0, 0), and the coordinates of the lower
right corner are (511, 341). Note that the coordinate numbering system
starts with 0.
In high school when you learned about the Cartesian coordinate
system, higher numbers for the vertical coordinate of a point meant that
the point was closer to the top of the graph or picture. On the Macintosh,
OT
UNLEASHING THE MAGICIAN
larger numbers for the vertical coordinate indicate that the pixel is closer
to the bottom of the screen, just the opposite of the Cartesian coordinate
system.
We are using Macintosh Pascal for all programming examples, and it
always draws in a window on the Macintosh screen. In most cases the
window will be somewhat smaller than the screen. The coordinates that
we use will be relative to the upper left corner of the inside of the window;
that is, the coordinate of the upper left corner of the window’s interior
(not the window frame) is (0, 0).
Let’s see what a simple program to draw a line looks like (listing 1.1).
The MoveTo statement tells the Macintosh where to start the line. The
LineTo statement tells it the end point for the line. When we run the
program, it draws the line shown in figure 1.4.
Listing 1.1 DrawLine
Program DrawLine;
{listing 1.1}
begin
Moveto(10, 15);
LineTo(110, 142)
end.
ANT
0S drawing SSS
Figure 1.4 The line
OA
JAGGIES 7
JAGGIES
NA
The line that we drew in figure 1.4 looks a little strange. It’s not exactly a
straight line. Let’s look at an enlargement (figure 1.5) and see if we can
figure out what’s wrong with it.
The pixels that make up the line don’t fall exactly on the line. More
correctly, the line falls between the pixels. The QuickDraw LineTo routine
turned on the pixels closest to the line. The jagged appearance of the line
is caused by the fact that the screen is made up of discrete pixels; a line
drawn by turning on pixels is only an approximation of a straight line
drawn with pencil and paper. This effect is called jaggies or, if you want
to sound more technical, aliasing. More sophisticated (and more expen-
sive) computer graphics displays can minimize the effects of aliasing by
varying the intensity of pixels adjacent to the line. We really can’t do that
on the Macintosh. We’re stuck with aliasing on some lines.
On the Macintosh, there is no aliasing on lines that are exactly
vertical, exactly horizontal, or at a 45-degree angle (figure 1.6). The
only thing we can do about jaggies is to try to design our pictures with a
minimum number of lines that are not vertical, horizontal, or at
45 degrees.
If you really need to have lines at other angles, don’t worry about it.
It doesn’t look that bad, especially if you look from across the room and
squint. I’ve been told that you can also remove jaggies by internal use of
enough tequila, but I can’t recommend that. The lines look fine, but
walking becomes a problem.
Figure 1.5 The line enlarged
MMT
UNLEASHING THE MAGICIAN
Figure 1.6 Lines and aliasing
MTT
HALFTONE IMAGES
MTT
So far we have seen how to draw lines by turning pixels on and off, but that
makes our drawing capabilities pretty limited. We would like to be able to
draw objects in various colors and shades. Even though some of the
Macintosh’s built-in software allows you to specify colors, the Macintosh
display can show only black and white. We can, however, use a newspaper
printing trick to make images appear to be drawn in various shades of gray.
If you look closely at a newspaper photograph, you can see that it is
made up of a collection of dots. Each dot is the same shade of gray
(actually, they are all black), but they are different sizes. By varying the size
of the small dots that make up an image, you can make the image appear
to be drawn in shades of gray. Your eyes and brain blend the dots together.
A picture using this method of producing shades of gray is called a
halftone image.
We can’t draw dots of different sizes on the Macintosh because all of
the pixels are the same size. We can do something nearly as good. We can
vary the number of dots that we turn on in a given area of the screen. Take
a look at figure 1.7. You can see several shades of gray. The enlarged
Figure 1.7 Shading patterns
PROGRAMMING 9
portion shows that more pixels are turned on in the darker shaded areas.
The built-in software in the Macintosh allows you to dictate specific
patterns that determine which dots are turned on. You can then use other
routines to fill areas of your picture with those patterns.
QUICKDRAW
AMA
I’ve mentioned the Macintosh’s built-in software several times, so let me
talk about it in a little more detail. The Macintosh has a great deal of
built-in software in read-only memory (ROM) chips in the machine. This
software provides the tools for programmers to write programs that fit
into the standard Macintosh user interface using menus, windows, text
fonts, and so on. The section of software that provides the tools for
drawing pictures and text on the screen is called QuickDraw.
QuickDraw can be a complex subject if you try to learn all of it at
once, so I will introduce QuickDraw routines and other information only
as we need it. There’s more to QuickDraw than you will see in this book,
and if you are interested in exploring it further, you can find a copy of the
QuickDraw manual in the Macintosh Pascal Technical Appendix or in
Apple Computer’s publication, Inside Macintosh.
All of the programs that we use in this book will be in Macintosh Pascal.
It’s an interpretive language that is easy to use for experimentation. What’s
more, it can access the QuickDraw routines in the Macintosh ROM.
Macintosh Pascal comes with a technical appendix that includes the
complete documentation for QuickDraw.
You can try most of these examples with any language that allows
access to the QuickDraw routines. Some languages (Microsoft BASIC is an
example) don’t let you access QuickDraw directly but provide program
statements or subroutines that do a subset of the QuickDraw functions. If
you want to be able to try all of the techniques in this book, you would
be better off using a language that allows direct access to all of the
QuickDraw routines.
There are other interpretive languages that allow access to the Quick-
Draw routines. FORTH is one example. Most compiled languages let you
call QuickDraw routines, but they are somewhat more time-consuming
and are not as well suited to experimenting and prototyping as is Macin-
tosh Pascal.
10
UNLEASHING THE MAGICIAN
In this book we focus on how to do graphics and sound. There is very
little information about how to program in Pascal or how to run the Pascal
interpreter. If you have a copy of Macintosh Pascal, you will find that type
of information in the documentation that comes with the software. If not,
you may want to investigate other books on Macintosh Pascal. The first
book in this series, Programming the Macintosh: An Advanced Guide, has
a chapter on Macintosh Pascal that is a good introduction to the language.
CAN
CHAPTER
© DRAWING IN TWO
DIMENSIONS
Dimensions
Coordinate Systems
Pixels and Memory
The Pen
Coordinate Transformations
Points and Rectangles
Clipping and Windows
SOA
11
12
OT
DRAWING IN TWO DIMENSIONS
TT
In one sense, all of the drawing that we will do is two-dimensional; we will
always draw on a flat surface (the Macintosh display or the printer). I
use the term two-dimensional when talking about representing a two-
dimensional—that is, flat—image on the Macintosh. I use the term three-
dimensional when we are representing a three-dimensional image by
drawing it in perspective views on the Macintosh display.
In fact, the vast majority of Macintosh applications do strictly two-
dimensional drawing. Only a very specialized application will draw three-
dimensional perspective views. Two-dimensional drawing techniques are
still the basis for three-dimensional drawing programs. In the end, they
must represent the three-dimensional object by drawing it in two dimen-
sions on the display, and they do that by using the two-dimensional
techniques discussed in this chapter.
COORDINATE SYSTEMS
All computer graphics programs are based on fundamental principles of
mathematics and geometry. The more rigorous their application of math-
ematical tools, the better the resulting software, so QuickDraw has a very
exactly defined mathematical basis. I will introduce the mathematical
concepts and definitions at appropriate times in the book. Taken a piece
at a time, the definitions may seem arbitrary or restrictive, but after I’ve
explained each with examples and shown how they fit together, you’ll
appreciate their rigor.
The first of these concepts is the QuickDraw coordinate system. In
the preceding chapter, I discussed pixel coordinates and rather loosely
defined the horizontal and vertical coordinates of a pixel. The QuickDraw
coordinate definition is similar but more exact. Let’s see an example.
In figure 2.1, we see an array of pixels on the Macintosh screen set
into a grid, a network of horizontal and vertical lines. The lines are
between the pixels and represent the QuickDraw coordinate system. A
pair of QuickDraw coordinates is a pair of numbers specifying a horizontal
and a vertical coordinate. The coordinates represent the intersection of
two of the lines in figure 2.1. The coordinate system actually specifies
points between the pixels.
That’s all very nice, but what we ultimately want to do is use the
coordinate system to specify a pixel, not a pair of intersecting imaginary
lines. The coordinate pair in the diagram, (3, 5), identifies the intersection
of two lines in the coordinate system, and they identify a single pixel, the
CANA
THE PEN 13
0123456789
FIEIEIE] a
Soovsanaewn-o0
Coordinates (3,5)
Figure 2.1 Pixels and coordinates
AAT
one immediately below and to the right of the intersecting lines (the
darkest pixel in figure 2.1).
PIXELS AND MEMORY
CIN
The data that turns a pixel on or off is actually stored in a section of
memory in the Macintosh called the display RAM. There is 1 bit of display
RAM for each pixel on the screen. The Macintosh display hardware
automatically reads the display RAM and uses the data to turn pixels on the
screen on and off. Turning on a bit in the display RAM causes the
Macintosh’s display hardware to light the associated pixel.
The Macintosh’s memory is organized into 8-bit bytes, but the screen
is usually organized into windows of arbitrary size. There are other
QuickDraw definitions that tell us how to find a pixel in the Macintosh
memory, what the active drawing area on the screen is, and what coor-
dinate system is being used in that drawing area. For now, we'll just ignore
all of that and assume that we are always drawing in a window in which
the coordinates of the upper left pixel are (0, 0).
THE PEN
Let’s take another look at the routine we used to draw a line (listing 2.1).
You tell QuickDraw how to draw something by describing how to
draw it as if you were using a pen on a piece of paper ruled with the
coordinate system. You tell QuickDraw where to move the pen and
14
AIM
DRAWING IN TWO DIMENSIONS
Listing 2.1 DrawLine
program DrawLine;
{listing 2.1}
begin
Moveto(10, 15);
LineTo(110, 142)
end.
whether to put the pen down on the paper, drawing as it moves, or to lift
the pen up and just move it without drawing. The MoveTo statement
moves the pen to the starting point. The LineTo puts the pen down on the
Paper and moves it to the end point, drawing a line.
Let’s draw something a little more ambitious. We’ll draw a square,
this time putting the coordinates in variables instead of having the actual
numbers in the calls to the drawing routines (listing 2.2, figure 2.2).
By putting the coordinates in variables, we can do some processing
on them before we call the drawing routines and vary the size, location,
and orientation of the object we are drawing.
Listing 2.2 DrawBox
Program DrawBox;
{listing 2.2}
var
vl, hl, v2, h2, v3, h3, v4, h4 : INTEGER;
begin
hl := 20;
vl := 20;
h2
v2
h3 := 80;
v3 := 80;
h4
v4
Moveto(hl, vl);
LineTo(h2, v2);
LineTo(h3, v3);
LineTo(h4, v4);
LineTo(h1, v1);
end.
NT
COORDINATE TRANSFORMATIONS 15
Drawin:
Figure 2.2 The square box
AMAT
COORDINATE
TRANSFORMATIONS
Suppose we wanted to draw the box again but in a different location in the
window, stretched in one direction or rotated. We can do all of those
things using coordinate transformation formulas. There are three basic
coordinate transformations: translation, scaling, and rotation. Transla-
tion is moving a point (or each point in an object) from one screen location
to another. Scaling is changing the scale in either the vertical or horizontal
direction. Changing the scale in one direction causes the object to shrink
or stretch in that direction.
Let’s see how we would do a coordinate translation. We will draw the
box again but further over to the right and a little lower in the display
window. We could figure out the new coordinates by hand and add them
to the coordinates of each corner of the box, or we can let Pascal figure
them out for us. We can calculate the new coordinates of a point we are
translating (moving) by adding or subtracting the distance we want to
move it. After we add the coordinate translation, the program is as shown
in listing 2.3.
The distance to move the box (in numbers of pixels) is in the DeltaH
and DeltaV variables. We just add DeltaH and DeltaV to the coordinates of
each corner of the box.
We can change the drawing scale in either the horizontal or vertical
direction by multiplying the final coordinates of each point by the scale
factor. If we want to shrink the box to three-fourths of its original size in
16
RO
DRAWING IN TWO DIMENSIONS
Listing 2.3 DrawBox with Translation
program DrawBox;
{Listing 2.3)
var
vl, hl, v2, h2, v3, h3, v4, h4 : INTEGER;
DeltaH, DeltaV : INTEGER;
begin
{set initial coordinate values}
hl 20;
vi 207
h2 20;
v2 80;
h3 80;
v3 80;
h4 80;
v4 207
{set transformation parameters}
DeltaH 4S;
Deltav -10;
{perform coordinate transformation}
hl
vi
h2
v2
h3
v3
h4
v4
Moveto (hl, vl);
LineTo(h2, v2);
LineTo(h3, v3);
LineTo(h4, v4);
LineTo(hl, vl);
end.
the horizontal direction, we would modify the program to include a scale
factor of 0.75. In the next version of the program (listing 2.4), we add scale
factors for both directions and convert the coordinate transformation
calculation into a subroutine.
Note that the scale factors are real numbers (floating-point), but the
coordinates are integers. Many of the calculations that we must do to
transform coordinates can be done only with real numbers in Pascal, but
the results are pixel coordinates, and they are always integers. The trans-
form routine adds two integers, the coordinate (h or v) and the translation
value (HDelta or VDelta). The result is an integer. The routine multiplies
that integer by a real number, and the result is a real number. The routine
uses the Round function to convert that real number to an integer. The
Round function returns a long integer value, but Pascal allows you to
COORDINATE TRANSFORMATIONS 17
AIIM Listing 2.4 DrawBox with Translation and Scaling
program DrawBox;
{Listing 2.4}
var
vl, hl, v2, h2, v3, h3, v4, h4 : INTEGER;
DeltaH, DeltaV : INTEGER;
ScaleH, ScaleV : REAL;
Procedure transform (var h, v : INTEGER;
HDelta, vDelta : INTEGER;
HScale, VScale : REAL);
begin
{do coordinate translation and scaling}
Round((h + HDelta) * HScale);
Round((v + VDelta) * VScale);
begin
{set initial coordinate values}
hl := 20;
vi 207
h2 20;
v2 80;
h3
v3
80;
80;
h4 := 80;
v4 i= 20;
{set transformation, parameters}
DeltaH := 45;
DeltaV := -10;
ScaleH := 0.75;
Scalev := 1.0;
{transform coordinates}
transform(hl, vl, DeltaH, Deltav, ScaleH, ScaleV);
transform(h2, v2, DeltaH, DeltaV, ScaleH, ScaleV);
transform(h3, v3, DeltaH, DeltaV, ScaleH, ScaleV);
transform(h4, v4, DeltaH, DeltaV, ScaleH, ScaleV);
{Draw a Box}
Moveto (hi,
LineTo (h2,
LineTo(h3, v3);
LineTo(h4, v4);
LineTo(hl, vl);
end.
assign a long integer value to an integer if the number is not too large to
store in an integer.
What we have done so far with coordinate transformation is simply to
move an object’s location on the screen. If we recalculated the position of
every object on the screen, the effect would be the same as if we moved
the entire picture relative to the coordinate system. In some cases we want
to move the coordinate system but keep the picture in the same location.
18 DRAWING IN TWO DIMENSIONS
For instance, if for some reason we needed to redefine the coordi-
nates of the upper left corner of the screen to be (40, 60) instead of (0, 0),
we would in effect be moving the coordinate system to the left 40 pixels
and up 60 pixels. Then the origin of the coordinate system, the point
(0, 0), would not be on the screen. In figure 2.3, we see the coordinate
system moved so that the origin is off the screen.
The Point (0,0) The Point (40,60)
The Rectangle(60,70,90,90)
Before Moving the Origin
The Point (0,0)
The Point (40,60)
The Rectangle(60,70,90,90)
After moving the Origin
IMI Kiguee 2.3 Coordinate system translation
COORDINATE TRANSFORMATIONS 19
If we want to draw our objects in the same locations on the screen
but using the new coordinate system, we must convert the coordinates
of each object to the new coordinate system by adding 40 to all of the
horizontal coordinates and adding 60 to all of the vertical coordinates.
Sometimes we move a coordinate system because it is more conve-
nient for doing a particular calculation. The scaling calculation that we did
in our coordinate transformation routine doesn’t really work well for
scaling objects. The way we wrote the routine, the scale factor affects the
object’s position on the screen as well as its size. There are several
methods that we could use to scale an object properly, but one method
requires that the object be centered on the origin of the coordinate
system. If we want to scale just one object, we perform a coordinate
system translation to move the origin of the coordinate system to the
center of the object, perform the scaling calculation, and then move the
coordinate system back to where it was. In chapter 7 we will see how this
same technique is used in doing the calculations to rotate an object about
an arbitrary point.
When we work in a window, our program draws pictures using a
coordinate system that has the origin at the upper left pixel in the window.
That pixel is not the origin in the Macintosh screen coordinate system. The
Macintosh QuickDraw software translates the coordinates that we use in
drawing commands (in the coordinate system of our window) to the
coordinate system of the Macintosh screen. It uses methods similar to the
method we used to move our box around on the screen.
Fortunately for us, QuickDraw has a lot of built-in routines for
handling things like converting from one coordinate system to another or
moving an object by changing its coordinates. (For instance, we’ll shortly
be using the OffsetRect routine, instead of our own coordinate translation
routine, to move a rectangle.)
MapRect and MapPt are two of the QuickDraw routines that perform
coordinate conversion. The MapRect routine performs coordinate system
conversion doing both translation and scaling of a rectangle.
MapPt converts the coordinates of a point in one rectangle to the
coordinates of another rectangle. It performs translation and scaling so
that the point ends up in the same relative location in the destination
rectangle. If you used MapPt to convert the coordinates of a point in the
center of a rectangle to the coordinates of a point in a destination rectangle
that was twice the size of the source rectangle, the point’s new coordi-
nates would be in the center of the destination rectangle.
You won’t find MapPt and MapRect in the section of the QuickDraw
manual on points and rectangles; they are in the miscellaneous utilities
section. We'll take a closer look at MapRect and MapPt in chapter 6.
I
DRAWING IN TWO DIMENSIONS
QuickDraw has other routines for converting coordinates from the
coordinate system of one window to the coordinate system of another
window or a print buffer. We will take a closer look at those when we get
into QuickDraw’s GrafPort data structure and GrafPort coordinate systems
in chapter 6.
POINTS AND RECTANGLES
We have been representing a point as a pair of integers, and that would
suffice for everything that we want to do, but it would be more convenient
to have a data type for representing a point. QuickDraw has a data type
called point. Its definition looks like this:
type
Point = record case INTEGER of
0: (v, h: INTEGER);
1: (vh : array [VHSelect] of INTEGER);
end;
By defining a point this way, we can refer to it as a pair of integers or
as an integer array of size 2. If we add the point type to our program, we
can get a better idea of how it is used. We will make a few other changes
also. The transform routine will be split into a translation function and a
coordinate transform routine (listing 2.5).
Note that we used the point data type but did not define it with a type
definition. The program ran anyway. How can we get away with that? The
answer is that Macintosh Pascal has all of the QuickDraw constants, types,
procedures, and functions predefined.
As of now, there seems little reason to split up the transform routine,
but we will find it more useful to have it split up when we do the object
rotation calculations. Wherever we used a point data type, we referred to
its coordinates as parts of a record rather than as elements of an array.
When we call the transform routine, we pass it a point, but when the
transform routine calls the translate routine, it passes an integer that is one
of the coordinates of a point (coord.v or coord.h).
Anyone who has already looked at the QuickDraw documentation
knows that we are really drawing this box the hard way. QuickDraw has a
data structure that describes a rectangle and a routine that will draw a
rectangle for us. Let’s take a look at those.
POINTS AND RECTANGLES 21
INUIMUIII! Listing 2.5 DrawBox with the Point Data Type
Program DrawBox;
{listing 2.5}
var
vl, hl, v2, h2, v3, h3, v4, h4 : INTEGER;
DeltaH, DeltaV : INTEGER;
ScaleH, ScaleV : REAL;
TopLeft : point;
BotLeft : point;
TopRight : point;
BotRight : Point;
function Translate (hv, Delta : Integer;
Scale : REAL) : INTEGER;
begin
{do coordinate translation and scaling}
translate := Round((hv + Delta) * Scale);
end;
procedure Transform (var coord : point;
HDelta, VDelta INTEGER;
HScale, vScale REAL) 7
{translate each coordinate of the point}
begin
coord.h translate(coord.h, HDelta, HScale);
coord.v translate(coord.v, VDelta, VScale);
end;
begin
{set initial ccordinate values}
TopLeft.h := 20;
TopLeft.v := 20;
BotLeft.v 20;
BotLeft.h 80;
BotRight.v 80;
BotRight.h 80;
TopRight.v 80;
TopRight.h := 20;
{set transformation parameters}
DeltaH 45;
Deltav -10;
ScaleH 0.75;
Scalev 1.0;
{transform coordinates}
transform(TopLeft, DeltaH, Deltav, ScaleH, Scalev);
transform(BotLeft, DeltaH, DeltaV, ScaleH, ScaleV);
transform(BotRight, DeltaH, DeltaV, ScaleH, ScaleV);
transform(TopRight, DeltaH, DeltaV, ScaleH, ScaleV);
{Draw a Box}
MoveTo(TopLeft.h, TopLeft.v);
LineTo(BotLeft.h, BotLeft.v);
LineTo(BotRight.h, BotRight.v);
LineTo(TopRight.h, TopRight.v);
LineTo(TopLeft.h, TopLeft.v);
end.
DRAWING IN TWO DIMENSIONS
type
Rect = record case INTEGER of
0 : (top : Integer;
left : Integer;
bottom : Integer;
right : Integer);
1: (TopLeft : point;
BotRight : point);
end;
The Rect data type can define a rectangle two ways. The first way lists
the vertical coordinates of the top and bottom and the horizontal coor-
dinates of the left and right sides. The other method defines the rectangle
by giving coordinate pairs for the upper left corner and the lower right
corner. Either way, it requires the same amount of memory to store a
rectangle: four integers.
QuickDraw has a collection of routines for drawing rectangles and
performing calculations with the rectangle data type. For now, we will use
only two in our program:
SetRect(var theRect : Rect, top, left, bottom, right : INTEGER)
SetRect sets the values of the fields in the rectangle data
structure to the integer values that you supply.
FrameRect(theRect : Rect)
FrameRect draws the rectangle as specified by the corner
coordinates in the rectangle data structure.
We could get by without the SetRect routine by setting the value of
each integer in the rectangle data structure individually, but it’s a little
easier to use the SetRect routine. Let’s see what our program looks like
now (listing 2.6).
It doesn’t look much like our old program. We’ve replaced most of
our variables with a rectangle variable and most of our program statements
with a couple of QuickDraw routines. In fact, if you look closely you will
see that we have eliminated the lower left corner and upper right corner
definitions from our program. They aren’t in the rectangle definition
because it doesn’t need them. You can define a QuickDraw rectangle by
specifying just two points, the upper left corner and the lower right corner.
QuickDraw uses rectangles extensively to define rectangular shapes,
the limits of other shapes, windows, the limits of drawing areas on the
screen, scale changes, and coordinate conversions, to name just a few.
POINTS AND RECTANGLES
HMMM Listing 2.6 DrawBox with SetRect
program DrawBox;
{listing 2.6}
var
DeltaH, Deltav
ScaleH, Scalev
theBox : Rect;
INTEGER;
REAL;
function Translate (hv, Delta : Integer;
Scale : REAL) : INTEGER;
begin
{do coordinate translation and scaling)
translate := Round((hv + Delta) * Scale);
end;
procedure Transform (var coord : point;
HDelta, VDelta : INTEGER;
HScale, VScale : REAL);
{translate each coordinate of the point}
begin
coord.h translate(coord.h, HDelta, HScale);
coord.v translate(coord.v, VDelta, VScale);
end;
begin
{set initial coordinate values}
SetRect (theBox, 20, 20, 80, 80);
{set transformation parameters}
DeltaH 45;
DeltaV := -10;
ScaleH := 0.75;
Scalev := 1.0;
{transform coordinates}
transform(theBox.TopLeft, DeltaH, DeltaV, ScaleH, ScaleV);
transform(theBox.BotRight, DeltaH, DeltaV, ScaleH, ScaleV);
{Draw a Box}
FrameRect (theBox)
end.
QuickDraw uses memory economically by defining a rectangle with two
points instead of four. There is a trade-off, though; conserving memory
places a fundamental limitation on the use of rectangles, and because
rectangles are used for so many things in QuickDraw, this same limitation
is placed on other things you do with QuickDraw.
The major thing that QuickDraw does not do is rotate images. It uses
rectangles to define the limits of all of the images it draws. It cannot rotate
a rectangle through an angle that is not a multiple of 90 degrees because
a rectangle that is not strictly horizontal and vertical cannot be fully
defined by only two corners.
24
IMA
DRAWING IN TWO DIMENSIONS
CLIPPING AND WINDOWS
A window on the Macintosh screen presents us with a limited area in
which to draw. For that matter, the Macintosh screen itself is a limited area.
‘What would happen if we drew off the screen? On some computers, a line
drawn off the screen on one side reappears on the opposite side of the
screen. In other computers, a line drawn off the screen is written into
an area of memory in which it can destroy data or programs. In any
case, writing outside a window or off the screen is something we don’t
want to do.
‘We want to make sure that we prevent our program from drawing
even part of an image outside a window. The act of limiting the drawing
area is called clipping. We need to clip our image to make sure it fits inside
the rectangle in which we are drawing.
How can we draw an object like a rectangle if we move part of it
outside the window? We could check each rectangle that we draw and
draw only the part of it that is inside the window. That would be difficult
with rectangles and worse with more complex objects.
QuickDraw comes to the rescue. It has a routine called ClipRect that
sets a clipping rectangle. The location of the clipping rectangle is stored in
QuickDraw’s internal data structures. QuickDraw then checks each pen
motion against the limits set by the clipping rectangle and doesn’t draw
outside of the clipping rectangle. When we first start drawing in the
Macintosh drawing window, the clipping rectangle is set to the window
location and dimensions. We can set the clipping rectangle to any size and
dimensions that we want in order to limit the drawing area to a portion of
the window.
Let’s modify our program to draw several rectangles. Then we'll add.
a call to ClipRect to limit the drawing area, and see what happens. Listing
2.7 shows the program set up to draw several rectangles. (Notice the use
of OffsetRect, as promised.) In figure 2.4, we see what the program draws.
Listing 2.8 shows where we added the ClipRect statement. Figure 2.5
shows the results of drawing while limited by the clipping rectangle.
CLIPPING AND WINDOWS
IM! Listing 2.7 DrawBox Modified to Draw Several Rectangles
program DrawBox;
{listing 2.7}
var
theBox : Rect;
begin
{set initial coordinate values}
SetRect (theBox, 20, 20, 80, 80);
{Draw a Box}
FrameRect (theBox) ;
{translate coordinates, moving the box}
OffsetRect (65, 0);
{Draw it again}
FrameRect (theBox) ;
{draw more boxes at different locations}
offsetRect (-65, 75);
FrameRect (theBox) 7
OffsetRect (65, 0);
FrameRect (theBox) ;
end.
SS tawing SSS
TINIAN Figure 2.4 Rectangles
A
DRAWING IN TWO DIMENSIONS
Listing 2.8 DrawBox with ClipRect
program DrawBox;
{listing 2.8}
var
theBox, Clipping : Rect;
begin
{set clipping rectangle}
SetRect (Clipping, 40, 40, 120, 135);
ClipRect (Clipping) ;
{set initial coordinate values}
SetRect (theBox, 20, 20, 80, 80);
{Draw a Box}
FrameRect (theBox) ;
{translate coordinates, moving the box}
OffsetRect (theBox, 65, 0)7
{Draw it again}
FrameRect (theBox) ;
{draw more boxes at different locations}
offsetRect (theBox, -65, 75);
FrameRect (theBox) 7
OffsetRect (theBox, 65, 0);
FrameRect (theBox) 7
end.
CANA
== Drawing
fa
Figure 2.5 Clipped rectangles
CN
CHAPTER
<> DRAWING SHAPES
AND PATTERNS
Coordinates and the Pen
The Pen Pattern
Drawing Modes
QuickDraw Shapes
Drawing QuickDraw Shapes
AA
DRAWING SHAPES AND PATTERNS
COORDINATES AND THE PEN
IM
Remember our picture of the coordinate system and pixels from chapter
1? The coordinates actually run between the pixels. The coordinate system
determines where the pen goes when it draws. The pen can actually be
larger than a pixel; you can set the size of the pen yourself by using
QuickDraw’s PenSize procedure. The pen is shaped like a rectangle, and
each side is an integral number of pixels in length, from 0 to 32,767.
The coordinates of the pen determine the location of the upper left
corner of the pen’s rectangular shape. You can imagine the pen as having
a grid with squares the same size as pixels. Every time you draw with the
pen, it stamps down on the screen’s pixels like a rubber stamp and leaves
its mark.
Up to now, we have used the default pen size, 1 pixel by 1 pixel. It
covered a single square, and when we positioned the pen at a particular
pair of coordinates, it landed on the pixel below and to the right of the
coordinate system lines (figure 3.1).
If we define a pen size of 8 by 8 pixels, the coordinates of the pen will
determine the location of the upper left corner of the pen rectangle. The
pen will mark the pixels in the 8-by-8 square whose upper left pixel lies
immediately to the right and below the coordinate system lines; that is, the
pen marks the pixels immediately under the squares in the pen rectangle
(figure 3.2).
0123456789 [lH] Pen
oO
‘ la
z
Oooo o
OOo a
SOG ol
OOO o
alele a
8
: cl
2 elelala a
Pen Coordinates (8,2)
Figure 3.1 The 1-by-1 pen within the coordinate system
THE PEN PATTERN 29
Pen
0124456789
SoovraHaewn-=0
Pen Coordinates (1,1)
Figure 3.2 The 8-by-8 pen within the coordinate system
THE PEN PATTERN
We now have in our minds an image of the pen stamping its way across the
screen, turning white pixels into black pixels, but it doesn’t have to work
that way. We can make the pen turn pixels black or white. We can do more
than that; we can make the pen lay down a predefined pattern as it moves.
A pattern is an 8-by-8 pixel sequence that repeats itself over some area
of the display. The gray background of the desk top is a pattern. If you have
used MacPaint, you have seen patterns that you can select along the
bottom of the screen. QuickDraw has four predefined patterns that you
can use (figure 3.3), or you can design your own.
The actual squares that are turned on or off on the pen are not the
same for every pen location. They change to keep the pen’s pattern aligned
with the last pattern stamped. The pen becomes more like a roller laying
down a pattern than a stamp that stamps the same thing every time it hits
the paper.
NT
DRAWING SHAPES AND PATTERNS
ItGray Gray
dkGray Black
Figure 3.3 Predefined patterns
Patterns are always aligned on 8-pixel boundaries. If you decide to
join two patterns that you have drawn near each other, you can just fill in
the area between them with more of the pattern. There’s no problem with
alignment. In figure 3.4, note how the two areas filled with the pattern
have been joined with perfect pattern alignment.
Let’s run a short program (listing 3.1) that sets the pen size and draws
some simple figures with three different pen patterns (figure 3.5).
We can also define our own custom patterns. A pen pattern is 8 pixels
by 8 pixels, so the first thing we should do to define a pen pattern is draw
an 8-by-8 grid and mark the squares (pixels) that we want to set. For our
example, we will define a pattern that can be used to draw a grid on the
screen. Our pattern is shown on its 8-by-8 grid in figure 3.6.
Now we need to define a variable of the type pattern. A pattern is a
64-bit variable defined thus:
type
Pattern = packed array [0..7] of 0..255;
It’s an 8-byte array. We don’t need to include the actual pattern definition
in our program, just the variable. The pattern data type is predefined along
with all of the other QuickDraw data types.
In our example program, we defined a variable called grid that is of
the pattern data type. Before we use the pattern, we must set the bits in the
pattern variable. To set the bits, we will use a FOR loop to set each byte
DRAWING MODES 31
loti tad
cal
inca
aot
not
Figure 3.4 Pattern alignment
I
in the 8-byte array. Setting a byte will set all of the pixels in one row of the
pattern; byte 0 sets the pixels in the top row, and byte 7 sets the pixels in
the bottom row. The bits in each byte correspond to the pixels in the same
order as you see them in the grid. The leftmost pixel has a bit value of 128;
the rightmost pixel has a bit value of 1. To set the rightmost pixel in each
of the first seven rows, we set the first 7 bytes of the array to 1. To set all
of the pixels in the last row, we set the eighth byte to 255 (all 8 bits on).
In the listing for the program (listing 3.2), you will see that we start
drawing the pattern 1 pixel to the left and 1 pixel above an 8-pixel
boundary (the boundary of a pattern on the screen). We do that in order
to make a complete grid. If we started on 8-pixel boundaries, we would
not include the top line and left line of the grid pattern that we draw. The
pattern that the program draws is shown in figure 3.7.
DRAWING MODES
Whether we are drawing a pattern or drawing solid black lines, we have
another means of controlling how the pen draws on the screen. In all of
the drawing we have done so far, the pen has either drawn a black line over
everything it crosses or laid down a pattern over everything it crosses. The
32
UT
DRAWING SHAPES AND PATTERNS
Listing 3.1 PenPatterns
program PenPatterns;
{Listing 3.1}
{Pen Pattern exercise}
procedure DrawBox;
begin
moveto(90, 10);
line(0, 20);
line (20, 0)7
line(0, -20)7
line(-20, 0)
end;
procedure DrawTriangle;
begin
moveto(100, 50);
line(-25, 50);7
line (50, 0);
line(-25, -50);
end;
procedure DrawLine;
begin
moveto(63, 130);
line (80, 0);
end;
begin
PenSize(3, 3);
PenPat (black);
DrawBox;
PenSize(8, 8);
PenPat (1tGray);
DrawTriangle;
PenSize(1, 18);
PenPat (dkGray) ;
DrawLine;
end.
pen pattern, whether solid black or something else, was copied onto the
pixels that the pen passed over.
It is possible to have the existing image on the screen affect the
drawing done by the pen. For instance, instead of copying the pattern to
the screen pixels, the pen can do a logical OR between the pen squares and
the screen pixels. The result would be that any black squares on the pen
would set screen pixels to black, but any white squares on the pen would
have no effect.
The pen has eight writing modes that are two sets of variations on
four basic writing modes. We’ve already seen the COPY mode; we’ve been
AMA
DRAWING MODES 33
i)
Figure 3.5 The result of the PenPatterns program
I
tala
Figure 3.6 A custom pattern
using it in our programs. It is the default pen mode. We just discussed the
OR mode. There’s also an XOR mode and a BIC mode. Programmers
should recognize the Boolean OR and XOR functions from their program-
ming experience.
With the OR, XOR, and BIC modes, the white squares on the pen do
not affect the pixels on the screen. In OR mode, the black squares on the
pen set the corresponding pixels under them on the screen to black. In
XOR mode, the black pen squares invert the pixels on the screen. BIC
mode does not correspond to a Boolean function. Like OR and XOR it
affects only the pixels under black pen squares. It sets the screen pixels
under the black pen squares to white.
We have four basic transfer modes now, COPY, OR, XOR, and BIC.
The remaining four modes are notCOPY, notOR, notXOR, and notBIC.
They work like the first four except that the squares on the pen are treated
as if their values were inverted. The pen squares have an effect opposite the
one they had in the first four modes (table 3.1).
34
TT
DRAWING SHAPES AND PATTERNS
Listing 3.2 PenPatterns with a FOR Loop
program PenPatterns;
{Listing 3.2}
var
grid : pattern;
procedure InitPattern;
var
i: integer;
begin
for i := 0 to 6 do
grid(i] l;
grid[7] := 255;
end;
begin
InitPattern;
PenSize(1, 129);
PenPat (grid);
MoveTo (31, 31);
Line(128, 0);
end.
AN
EL drawing
i)
Figure 3.7 Another custom pattern
We can see a graphic illustration of pen modes with a little program
(listing 3.3). We’ll first define two patterns. The first pattern consists of
horizontal lines that we’ll draw using COPY mode. The other pattern will
be vertical lines, and we will draw that pattern on top of the first, using the
various pen modes.
DRAWING MODES 35
IAIN! “Table 3.1 Pen Modes
Pen mode Pen square Screen pixel Resulting screen pixel
patCOPY Black Black Black
patCOPY White Black White
patCOPY Black White Black
patCOPY White White White
notPatCOPY Black Black White
notPatCOPY White Black Black
notPatCOPY Black White White
notPatCOPY White White Black
patOR Black Black Black
patOR White Black Black
patOR Black White Black
patOR White White White
notPatOR Black Black Black
notPatOR White Black Black
notPatOR Black White White
notPatOR White White Black
patXOR Black Black White
patXOR White Black Black
patXOR Black White Black
patXOR White White White
notPatXOR Black Black Black
notPatXOR White Black White
notPatXOR Black White White
notPatXOR White White Black
patBIC Black Black White
patBIC White Black Black
patBIC Black White White
patBIC White White White
notPatBIC Black Black Black
notPatBIC White Black White
notPatBIC Black White White
notPatBIC White White White
36 DRAWING SHAPES AND PATTERNS
KM! Listing 3.3 ModesExperiment
Program ModesExperiment;
{Listing 3.3}
var
hStripes, vStripes : Pattern;
GraphRect : Rect;
procedure InitPatterns;
var
i: Integer;
begin
for i := 0 to 7 do
vStripes[i] 15;
hStripes [0] 2557
hStripes[1] := 255;
hStripes[2] := 255
hStripes[(3] := 255;
end;
procedure DrawStrip (Modes : BOOLEAN;
StartMode : INTEGER);
var
i: Integer;
{the modes are numbered 8-15 starting with patCopy}
begin
for i := 0 to 3 do
begin
if Modes then
PenMode(i + StartMode)
else
PenMode (patCopy) ;
Line(31, 0);
move (33, 0)7
end;
end;
procedure DrawStripes (DoModes : BOOLEAN);
begin
MoveTo(8, 112);
DrawStrip(DoModes, patCopy);
MoveTo(8, 160);
DrawStrip(DoModes, notPatCopy);
end;
procedure DrawPatterns;
begin
PenPat (HStripes) ;
Moveto(8, 16)7
DrawStrip(FALSE, PatCopy);
PenPat (vStripes) ;
MoveTo(8, 64);
DrawStrip(FALSE, PatCopy);
Continued
DRAWING MODES 37
IMM Listing 3.3 Continued
end;
begin
SetRect (GraphRect, 50, 50, 310, 270);
SetDrawingRect (GraphRect) ;
ShowDrawing;
InitPatterns;
PenSize(1, 32);
DrawPatterns;
PenPat (hStripes) ;
DrawStripes (FALSE) ;
PenPat (vStripes) ;
DrawStripes (TRUE) ;
end.
Looking at the first three statements in the main part of the program,
we see a call to SetRect to set values in a rectangle data structure, followed
by two unfamiliar statements, SetDrawingRect and ShowDrawing. We use
those three statements to set the size of the drawing window and make it
the active window. The default drawing window size just isn’t large
enough to display the output of our program.
The two unfamiliar statements are Macintosh Pascal procedures that
control the drawing window. The SetDrawingRect statement sets the size
and location of the drawing window to the rectangle GraphRect. The
values in GraphRect are in Macintosh screen coordinates (global coordi-
nates). The origin of that coordinate system is the upper left corner of the
screen.
The ShowDrawing procedure makes the Macintosh Pascal drawing
window the active window (it overlays the other windows on the screen).
Our program works with two patterns. It draws a series of eight
identical copies of the first pattern (horizontal stripes). Then, with the
pen, it draws the second pattern (vertical stripes) over the first pattern,
using the eight different drawing modes. The program first draws four
copies of the first pattern that we put on the screen; then, on the next line,
it draws four copies of the second just so we can see what they both look
like. It then draws the series of eight pattern combinations, using the eight
pen modes.
What interests us most about this program are the results (naturally)
and the routine that selects which mode to use. The DrawStrip routine
draws a strip of four patterns. It has two parameters: Modes (Boolean) and
StartMode (Integer). If Modes is FALSE, the procedure does not use the
various drawing modes; it just draws four copies of the current pattern,
using the patCopy pen mode. The program uses DrawStrip with Modes =
DRAWING SHAPES AND PATTERNS
FALSE for drawing the first two strips of patterns and for laying down the
first pattern in the second two strips.
If Modes is TRUE, the procedure draws four copies of the current
pattern, using the four different drawing modes. The drawing mode is
specified as an integer from 8 to 16. The StartMode parameter is an integer
that contains the drawing mode to be used for drawing the first copy of the
pattern. The drawing mode integer is incremented as we draw each of the
four patterns.
It may seem strange to start the drawing mode number with 8, but it
works out that way because the numbers 0 through 7 are used for another
type of drawing mode. PenMode uses the numbers 8 through 15.
Drawing mode Integer
patCOPY 8
patOR 9
patXOR 10
patBIC 11
notPatCOPY 12
notPatOR 13
notPatXOR 14
notPatBIC 15
The results of using the pen modes show up in the third and fourth
strips of patterns (figure 3.8). The third strip has the modes patCOPY,
patOR, patXOR, and patBIC. The fourth strip has notPatCOPY, notPatOR,
notPatXOR, and notPatBIC.
We would use the patCOPY mode when we want to eliminate
whatever image we may be writing over. We use the patOR mode when we
want to draw an image that intersects with, but does not eliminate,
another image. A good example is drawing a grid in a computer-aided
design program. If you give your program the capability of showing the
grid or not showing it, the user may elect to show the grid when a drawing
is already on the screen. Drawing the grid with pen mode patOR will put
the grid on the screen without disturbing the existing drawing. Where the
grid is black (at the grid lines), it turns pixels black. Where the grid is white
(between the grid lines), it does not alter the pixels.
If you are drawing one image over another, you may want to be able
to identify the areas where the images intersect. If you draw one image
over another using patXOR, in the areas where the images coincide (are
both black) the pixel values are inverted (turned white). The intersecting
parts of the two drawings look like a photographic negative. Sometimes it
is useful to have a cursor behave that way. If you have a cross-hair cursor,
ANT
DRAWING MODES 39
SSS wing BSS
lll
lll BE
[al
Figure 3.8 The result of the ModesExperiment program
you still want to be able to identify the cross point even though it may be
over a black portion of the drawing. Using patXOR to draw your cursor
will cause the cursor to appear black against the white portions of the
screen and white on the black areas.
The patXOR mode has an interesting property. If you draw over
another image with patXOR, you can restore the old image to its original
condition by again drawing the same new image in the same location with
patXOR. Drawing with patXOR a second time reverses the effect of the first
drawing. If you are drawing a cursor, draw it once with XOR to put it on
the screen, and draw it again with XOR to remove it so you can draw it in
another location.
MacPaint uses an XOR mode to draw the cursor and brush shapes
when you are moving them about without pressing the mouse button.
When you press the mouse button, MacPaint switches to COPY mode to
draw on the screen.
To give you another look at what the various PenModes do, I’ve
modified the program slightly to change the second pattern to a set of
small squares. The new pattern is called Blocks, and its initiation routine
is shown in listing 3.4. The result of drawing the Blocks pattern over the
horizontal stripes pattern with all eight modes is shown in figure 3.9.
40
CAN
DRAWING SHAPES AND PATTERNS
Listing 3.4 The Blocks Initiation Routine
{listing 3.4}
for i := 2 te 5 do
Blocks[i] := 60;
OMT
Caving aaa
Cand
Canad
paced
Cad
Saas
SE
Figure 3.9 Blocks drawn over horizontal stripes with all eight modes
TT
QUICKDRAW SHAPES
Using QuickDraw, you can draw objects by drawing a series of line
segments or by using the QuickDraw predefined shapes. The QuickDraw
predefined shapes and the QuickDraw routines that manipulate them give
you a powerful set of tools for drawing on the Macintosh. There are,
however, several limitations inherent in the design of QuickDraw. Quick-
Draw deals strictly with two-dimensional shapes, and you cannot rotate a
QuickDraw shape through an arbitrary angle.
If the object you want to draw can be represented by QuickDraw
shapes and you don’t need to rotate it, the QuickDraw shapes are the way
to go. QuickDraw has many routines for manipulating these shapes. They
are easier to define, draw, fill, move, and otherwise manipulate than
shapes made up of line segments.
The QuickDraw predefined shapes are the rectangle, round rectan-
gle, oval, and wedge (arc). QuickDraw also manipulates polygons (arbi-
trary shapes made up of line segments) and regions (objects of arbitrary
QUICKDRAW SHAPES a1
shape, not necessarily composed of line segments). The methods used to
define and manipulate regions and polygons are a little more complex than
for the other QuickDraw shapes, and I will put off discussing them until
chapter 5. Let’s take a look at the simple QuickDraw shapes (figure 3.10).
The rectangle should be familiar by now. We define it using the same
rectangle data structure we used before.
The oval is a little different. It has the shape of an ellipse but is not
defined the way you would expect an ellipse to be defined. A mathema-
tician would define an ellipse by specifying the coordinates of its foci and
the lengths of its major and minor axes. In QuickDraw, you define an oval
by specifying a rectangle whose sides just touch the outer limits of the oval
(figure 3.11). Note that since rectangles cannot be specified at arbitrary
angles to the coordinate system (they must be horizontal and vertical), an
oval must have its major and minor axes aligned with the coordinate
system. It cannot be tilted at an arbitrary angle.
©
Rectangle Oval
Pe
Round Rectangle Arc
CIN Figure 3.10 QuickDraw shapes
The Rectangle That Defines the Oval
Oval
HIN Figure 3.11 The oval
42 DRAWING SHAPES AND PATTERNS
What about a circle? The circle is a special case of the oval. To draw
a circle, you specify an oval whose defining rectangle is a square.
The round rectangle is simply a rectangle with rounded corners. You
define a round rectangle by specifying the rectangle that just touches its
sides and an oval that forms the shape of the rounded corners (figure 3.12).
In this case, you specify the width and height of the oval’s rectangle
instead of using a rectangle data structure to define the oval. To draw a
rounded rectangle, you need to pass the following data to a QuickDraw
routine:
Rectangle : Rect;
OvalWidth : INTEGER;
OvalHeight : INTEGER;
Note that all of the corners of a round rectangle are the same. You
cannot have different corners with different oval dimensions in the same
round rectangle.
Rounded Rectangle
The Rectangle That Defines the Round Rectangle
Width
=
Oval
Height
The Oval That Defines the Corner Curvature
IMM! Figure 3.12 The round rectangle
QUICKDRAW SHAPES 43
The arc (figure 3.13) looks like the curved edge of a slice of pie. It is
actually a section of an oval. To define an arc, you specify the rectangle
that defines the oval, the angle at which to start drawing the arc, and the
angle subtended by the arc.
The point of the arc is at the center of the rectangle. The angles of the
arc are measured relative to a vertical line from the center of the rectangle.
Arc angles are in degrees (MOD 360), not radians. Positive angles start at
the vertical line and go clockwise (figure 3.14). Negative angles are mea-
sured counterclockwise from the vertical line.
Here’s a Pascal versus QuickDraw incompatibility: the
predefined trigonometric functions in Pascal (sin, cos, tan,
and so on) measure angles in radians; QuickDraw measures
angles in degrees.
There you have them, the four QuickDraw shapes. Let’s see how to
draw them.
Arc Qual Arc Rectangle
Arc
KIMI Figure 3.13 The arc
Start Angle (45°)
)
INI Figure 3.14 Arc angles
44
DRAWING SHAPES AND PATTERNS
DRAWING QGUICKDRAW
SHAPES
You can draw each of the QuickDraw shapes five ways. The drawing
operations are frame, paint, erase, invert, and fill. Frame is the most basic
drawing operation. It draws an outline of the shape, using the current pen
size, mode, and pattern. When the drawing has been done, the pen returns
to the location it occupied before drawing the shape. None of the other
shape-drawing procedures change the pen location either.
The paint and fill operations are similar. The paint operation fills the
interior of the shape with the current pen pattern, using the current pen
mode. The fill operation fills the interior of the shape with a specified
pattern, not necessarily the current pen pattern. The pen draws the pattern
in COPY mode; that is, the pixels inside the rectangle are replaced with
pixels from the pattern.
Erase works like paint except that it uses the current background
pattern instead of the current pen pattern. The default background pattern
is white, so if you don’t set the background pattern, erase fills the shape
with white, exactly as you would expect.
Paint Fill Erase
Current pen pattern Specify a pattern Current background
pattern
Current pen mode COPY mode COPY mode
The invert operation inverts every pixel inside the shape. It converts
the interior of the shape to a negative image of itself.
Now you have four shapes and five ways to draw each. To form the
name of a procedure to draw a shape, you combine the name of the
drawing operation with the name of the shape.
A B
Erase Arc
Fill Oval
Frame Rect
Invert RoundRect
Paint
Take one from column A and one from column B (no egg roll).
DRAWING QUICKDRAW SHAPES 45
To frame a round rectangle, you use the procedure FrameRoundRect.
When you use one of the shape-drawing procedures, you must supply it
with the parameters that define the shape you are drawing. The list below
shows statements that use the paint operation to paint each of the four
shapes, and how the shape is defined in each case.
PaintRect(aRectangle : rect);
PaintRoundRect(aRectangle : rect; OvalWidth, OvalHeight :
INTEGER);
PaintOval(OvalRectangle : rect);
PaintArc(ArcRectangle : rect; StartAngle, StopAngle : INTEGER);
If we were using the fill procedure instead of paint, we would also
need to supply a parameter that specifies the pattern to use, for instance:
FillRect(aRectangle : rect, aPattern : pattern);
To fill a rectangle called BigRect with the light gray pattern, we would
use the statement:
FillRect(BigRect, LtGray);
Let’s see how to use some of these procedures in a simple program
(listing 3.5).
The program draws a series of figures using various pen patterns, fill
patterns, and background patterns (for erasing). The DrawShapes routine
accepts the parameters DrawPat, FillPat, and ErasePat, the patterns used
for drawing, filling, and erasing. The program calls DrawShapes three
times, each time with a different set of patterns.
The rest of the program is straightforward. The Framelt routine
frames each shape, using the specified pattern. The Filllt routine fills
each shape with the specified pattern, and the Eraselt routine erases each
shape with the specified pattern. Each of these patterns draws a rectangle,
round rectangle, oval, and arc. Figure 3.15 shows the drawing window
after the shapes are framed. In figure 3.16, we see the shapes after they
have been filled.
One interesting thing to note when you run this program is that the
FrameArc routine draws just the curved part of the arc while the FillArc
and EraseArc routines draw the interior as well.
46 DRAWING SHAPES AND PATTERNS
KOM Listing 3.5 An Untitled Program
program Untitled;
{listing 3.5}
var
vStripes : Pattern;
GraphRect, aRect, OvalRect, RoundRect, ArcRect : Rect;
i: INTEGER;
procedure InitPatterns;
var
i: INTEGER;
begin
for i 0 to 7 do
vStripes[i] := 15;
end;
procedure Delay;
var
j + INTEGER;
jsq : LONGINT;
begin
for j 1 to 300 do
isq sqr(j)i
end;
procedure EraseIt (ErasePat : pattern);
begin
backPat (ErasePat) ;
EraseRect (aRect) ;
Delay;
EraseRoundRect (RoundRect, 32, 32);
Delay;
EraseOval (OvalRect);
Delay;
EraseArc(ArcRect, 0, 90);
Delay;
end;
procedure FillIt (FillPat : pattern);
begin
FillRect (aRect, FillPat);
Delay;
FillRoundRect (RoundRect, 32, 32, FillpPat);
Delay;
FilloOval(OvalRect, FillPat);
Delay;
FillArc(ArcRect, 0, 90, FillPat);
Delay;
end;
procedure FrameUp;
begin
Continued
DRAWING QUICKDRAW SHAPES 47
KM Listing 3.5 Continued
FrameRect (aRect) ;
Delay;
FrameRoundRect (RoundRect, 32, 32);
Delay;
FrameOval (OvalRect);
Delay;
FrameArc(ArcRect, 0, 90)7
Delay;
end;
procedure DrawShapes (FramePat, FillPat, ErasePat :
pattern);
begin
penPat (FramePat) ;
FrameUp;
Delay;
Fillit(Fillpat);
Delay;
Eraselt (ErasePat) ;
Delay;
end;
begin
SetRect (GraphRect, 50, 50, 270, 270);
SetDrawingRect (GraphRect) ;
ShowDrawing;
InitPatterns;
PenSize(8, 8);
SetRect (aRect, 16, 16, 16 + 64, 16 + 64)
SetRect (Roundrect, 120, 16, 120 + 64, 16 + 64);
SetRect (OvalRect, 16, 120, 16 + 64, 120 + 64);
SetRect (ArcRect, 120, 120, 120 + 64, 120 + 64);
for i := 1 to 20 do
begin
DrawShapes (black, vStripes, DkGray);
Delay;
DrawShapes (LtGray, vStripes, Black);
Delay;
DrawShapes (DkGray, vStripes, White);
Delay;
end;
end.
Note that when we use the fill procedure, it fills all of the interior of
the shape, even the part that the frame procedure drew. The frame
procedure draws the outside frame of the shape, but the frame starts at the
outside edge and extends into the interior by a distance equal to the pen
size. The frame routine draws the shape by running the pen around the
inside edge of the shape.
48 DRAWING SHAPES AND PATTERNS
——eee OO IE
ET ©
O >
fs
IMM! Figure 3.15 Framed shapes
Caving SSS
III Fig“ure 3.16 Filled shapes
CHAPTER
ra |
CAN
DRAWING TEXT
Displaying Text with Type Fonts
Type Style
Type Size
Font Files
Text Character Images
Kerning
QuickDraw and the Font Manager
QuickDraw Routines
Drawing Text
Getting Information about the Font
A Program to Draw a Font’s Character Set
OAC
AANA
DRAWING TEXT
DISPLAYING TEXT WITH
TYPE FONTS
Most personal computers have the text character shapes defined in the
hardware that controls the CRT display. Programs need only write the
ASCII character codes for each character to the display controller. The
controller takes care of drawing the characters. Macintosh programs that
put text on the screen (or printer) must use QuickDraw to draw the text
characters. On the Macintosh, text characters are treated as any other
image that you want to draw on the display.
Since the shape and appearance of text characters is under the control
of Macintosh software, the characters can have any shape that we choose
to define. The designers of the Macintosh came up with a set of terms and
definitions to describe text characters. The terms they chose are com-
monly used in the printing trade.
The term typeface means a set of characters all of the same general
appearance. A type font is a complete set of characters (the alphabet,
numbers, punctuation, and special symbols) that all belong to the same
typeface. They have a similar appearance.
Type fonts usually have distinctive names. Most fonts for the Macin-
tosh are named after cities. Let’s see some examples (figure 4.1).
Most of the fonts shown in figure 4.1 are proportionally spaced fonts.
The spaces allowed for the characters are proportional, not uniform. Each
character in the font has a specification for the amount of space it
occupies. The spacing varies to improve the appearance of the type. The
Monaco font is not proportionally spaced; the space allowed its characters
This is the Toronto font in the 12 point size.
This is the Los Angeles font in the 12 point size.
This is the Chicago font in the 12 point size.
This is the Geneva font in the 12 point size.
This is the New York font in the 12 point size.
This is the Monaco font in the 12 point size.
This is the Venice font in the 14 point size.
This is the London font in the 18 point size.
This is the Athens font in the 18 point size.
HOM Figure 4.1 ‘Type fonts
TYPE SIZE 51
does not vary. It is called a monospaced font. Monospaced fonts are used
to imitate the appearance of text printed by other computers, those that
can print only monospaced fonts. On the Macintosh, they are sometimes
useful in applications where data or text must be aligned in tables.
The terms typeface, type font, and type style are used interchangeably
in some documents. I will use the term type font to mean a complete
collection of characters of the same appearance. When we actually draw
a character from a type font, we can specify other attributes that affect its
appearance: the type size and the type style.
TYPE STYLE
IANA
The characters of a font may be drawn in several styles other than plain.
They still have the same general shape, but their appearance is nevertheless
different from that of characters of the same font drawn in the plain style.
Figure 4.2 shows examples of all of the Macintosh type styles for the New
York type font.
TYPE SIZE
IANA
The size of type is measured in points. A point is approximately 1/72 inch.
Most application programs use 12 points as the default type size. In other
applications, where the designers need to get more text on the page, they
New York 18 point plain
New York 18 point bold
New York 18 point italic
New York 18 point underline
New York 18 point outline
New York 18 point shadow
New York 18 point bold outline
Figure 4.2 Type styles
DRAWING TEXT
use a smaller type size—9 points in the case of MacTerminal. Figure 4.3
shows various type sizes.
A Macintosh type font is stored as a data file containing bit images of
the characters in the font. The font file usually has the bit images of all of
the characters for several different type sizes. If you want to draw char-
acters in a size that is not defined in the font file, QuickDraw will use a
scaling algorithm to scale down a larger type size or scale up a smaller type
size. The characters drawn on the screen will look better if you choose a
type size that is included in the font file. Most application programs tell
you which sizes are in the system’s font file by displaying them in the
outline style in the type size selection menu. Figure 4.4 shows the type size
menu from MacPaint. The sizes displayed in outline style are in the font
file; the sizes in plain text are created by scaling another size.
New York 9 point.
New York 10 point.
New York 12 point.
New York 14 point.
New York 18 point.
New York 24 point.
New York 36 point.
TMM Figure 4.3 Type sizes
Fontsize
HMMM Figure 4.4 The MacPaint type size menu
CA
TEXT CHARACTER IMAGES 53
CAM
The Macintosh keeps a set of fonts stored in the operating system. They are
actually in a disk file (the system resource file), but it doesn’t show up on
the desk top or ina disk directory window. When you buy new type fonts,
they come in a disk file. You must use the font mover utility to load them
from the disk file into the system resource file.
The system has a limited number of type fonts stored in the system
resource file. A type font takes up a lot of disk space, so there’s a practical
upper limit on how many fonts you can have ona system disk. You can add
or delete fonts from the system resource file using the font mover utility.
If you have the latest version of the system disk, you will find a FONT/DA
mover utility. It moves both fonts and desk accessories between the system.
resource file and external disk files.
TEXT CHARACTER IMAGES
‘We know now that the images of all of the characters of a font in a given
size are stored in the system resource file. If we want to draw ina size that
is not in the system resource file, QuickDraw will scale one of the existing
sizes of that font as it draws. That takes care of the font and size, but how
is the type style information stored? It isn’t.
All of the characters that QuickDraw gets from the system resource
file are in the plain text style. If you specify that text will be drawn in
another style, QuickDraw uses type style routines to change the shape of
the plain text characters.
Let’s take a closer look at some text characters drawn by QuickDraw.
Figure 4.5 shows the uppercase and lowercase y in the Geneva font as
drawn by QuickDraw on the Macintosh screen. The ascent line is the
highest point reached by any character in the font (in the current font size).
The base line is the lowest point for uppercase characters. Some lowercase
characters have a descender, a part of the character that goes below the
base line. The descent line is the lowest point reached by any character in
the font. The font height is the maximum height of any character in the
font, the distance between the ascent line and the descent line.
The image width is the width of the actual image drawn by Quick-
Draw. The character width includes the image width and the spacing
between characters. Each character is defined separately in the font file,
and each has its own definition for the amount of space to leave before
drawing the next character.
54 DRAWING TEXT
Character Character
Width Width
Ascent Line
F OmmeC)
r] oO
Character
Height
Base Line | ial
at 4
Descent Line Image
Image Width
Width
KAMIIMMIIN Figure 4.5 Text characters enlarged
The font file also contains a specification for the amount of space to
leave between lines of text. Typesetters call this leading. On the Macin-
tosh, the leading specification in the font file tells how many pixels to leave
between the descent line of a line of text and the ascent line of the next
line of text (figure 4.6).
The outer dimensions of a character are defined by the character
rectangle (figure 4.7). The font rectangle is similar; its height is the
maximum character height, and its width is the maximum image width.
When you draw a character with QuickDraw, you first use the
MoveTo routine to position the pen in the location where you want the
Leading
a
T
Coocrere
III Figure 4.6 Leading
KERNING 55
Bauer
Figure 4.7 Character rectangles
ONT
character to appear. You then call the DrawChar routine to draw the
character. It draws the character so that the character’s origin point is at
the starting pen location. The character origin is always on the base line
and is usually on the left edge of the character rectangle (figure 4.8).
After drawing the character, the pen is still on the base line but is to
the right of its starting position by an amount equal to the character width
(not the image width).
IN
Some type fonts allow the descending tail of one character to pass under
the preceding character. Sometimes they allow a lowercase character to
tuck itself under the roof of an uppercase character like a T. Typesetters
call the adjusting of space between characters kerning. Figure 4.9 shows
two kerned lowercase letters, one with a descender.
In figure 4.10, we see a blowup of the two characters and can see how
the tail of the 7 actually passes under the right edge of the a.
If we defined character origin and character rectangle the way we
have so far, we could not get the tail of the 7 drawn under the a. After
drawing the a, the pen would move by an amount equal to the character
Cheracter
Origin
Cheracter
Origin
H
Figure 4.8 Character origins
NTA
DRAWING TEXT
gj
Figure 4.9 Kerned characters
CN
Figure 4.10 Kerned characters enlarged
ANA
width of the a and then start drawing the /, starting at its origin. A font
designer can get around that limitation by offsetting the origin of the j to
the right of the left edge of the character rectangle (figure 4.11).
Now if we ask QuickDraw to draw an a followed by a j, it sets the
origin of the j on the base line at the first pixel after the intercharacter
space defined for the a (in this case, the a is followed by a 2-pixel space).
Because the origin is offset, when QuickDraw draws the tail of the j, it
passes back under the a. Figure 4.12 shows the two characters kerned and
shows the locations of their origins.
Most fonts that have kerned characters also have nonkerned versions
of the same characters. The nonkerned characters are what you get if you
just type the normal characters on the keyboard. Usually, to get a kerned
g
Character > $5
Origin
Figure 4.11 Offset character origins
CIA
QUICKDRAW AND THE FONT MANAGER 57
+
+
lalate
Cooorrrt
Figure 4.12 Enlarged kerned characters with origins
IN
character, you must hold down the option key and type the character.
Only a few fonts have kerned characters. When you use them, you must be
careful not to use a kerned character next to one that it will overlay.
A font designer must supply some kind of image for 256 possible
characters. Besides the usual uppercase and lowercase alphabetic charac-
ters there are numbers, punctuation marks, and special characters. Even
so, there will rarely be a need for 256 characters. The font designer can
specify an image for a default character for the font file, and it will be used
for any character that doesn’t have its own image definition in the file.
Most fonts use a square about the size of an average character rectangle for
the default character image.
GUICKDRAW AND THE
FONT MANAGER
Most of the routines that we will use to draw text characters are Quick-
Draw routines. There are also a few useful routines in the font manager.
QuickDraw calls the font manager to load fonts into memory from the
system font file, but we will occasionally use a font manager routine to do
such things as lock a font in memory so it cannot be purged, or find out
if a font of a particular size is in the font file. If the font file does not have
a font in the size we want to use, QuickDraw will have to use another font
size, scaled to the size it is trying to draw.
QuickDraw refers to fonts by number. The font manager has routines
to find the name of a font if we know the font number or find the number
if we know the name.
When we want to draw characters on some device (the ImageWriter,
for instance), QuickDraw, the font manager, and the device driver decide
what font size would be appropriate for drawing on the device. If we had
some text that we drew on the Macintosh screen in the 12-point size,
DRAWING TEXT
QuickDraw and the font manager would use the 24-point size scaled down
to 12 points for drawing on the ImageWriter. The Image Writer has a higher
resolution than the Macintosh display, and using the larger font size scaled
down results in higher-resolution fonts on the printer.
Normally, an application program would call the font manager rou-
tine, InitFonts, before drawing any text. We don’t need to do that with
Macintosh Pascal because the Pascal interpreter does it for us.
QUICKDRAW ROUTINES
QuickDraw has a set of routines for setting the font characteristics and
another set for actually drawing the font. If you don’t set the font charac-
teristics, QuickDraw uses the default settings: the application font
(Geneva), 12-point size, and plain text. Let’s start our exploration of
QuickDraw text drawing by looking at the procedures that set the font
characteristics.
TextFont(font : INTEGER);
You pass TextFont a font number, and it sets the current font to
that number.
TextFace(face : style);
TextFace sets the style in which the text will be drawn.
TextMode(mode : INTEGER);
TextMode sets the drawing mode, much like the pen mode that
we saw in chapter 3.
TextSize(size : INTEGER);
The TextSize procedure sets the font size for drawing text. If the
font you specified is not in the font file, QuickDraw will scale
another size.
After setting the font characteristics, we will need the QuickDraw
procedures that draw the text.
DrawChar(textChar : char);
DrawChar draws a single character with its base line at the
current pen location.
DRAWING TEXT 59
DrawString(textString : Str255);
DrawString draws a string of characters with the base line at the
current cursor location.
Both DrawChar and DrawString advance the pen by the character’s
width after drawing each character. Neither will do a carriage return, line
feed, or form feed or perform any other automatic formatting. The most
you can expect them to do is leave a space when they encounter a space
character.
hint! DRAWING TEXT
Let’s take a look at a simple program that uses the QuickDraw procedures
to put some text on the screen (listing 4.1).
The InitText procedure sets the font characteristics. The InitDrawing-
‘Window procedure sets up the drawing window the same way we did in
chapter 3. The main part of the program draws a text string in the drawing
window. We see the result in figure 4.13.
TIMMNI Listing 4.2 DrawFont in Preliminary Form
program DrawFont;
{Listing 4.1}
procedure InitDrawingWindow;
var
GraphRect : Rect;
begin
SetRect (Graphrect, 50, 50, 310, 270);
SetDrawingRect (GraphRect) ;
ShowDrawing;
end;
procedure InitText;
begin
TextFont (3);
TextFace([{])# {normal}
TextMode (srcOR) 7
TextSize (12);
end;
begin
InitText;
InitDrawingWindow;
MoveTo(10, 20);
DrawString('The Macintosh Character Set');
end.
CT
DRAWING TEXT
Drawing
The Macintosh Character Set
[a]
Figure 4.13 The result of the preliminary DrawFont program
Note that before drawing, we set the pen location to (10, 20). Your
first thought might be to set it at (0, 0). That would work for the horizontal
coordinate; it would make the first character flush with the left edge of the
window. It wouldn’t be beautiful, but it would be readable. The problem
is with the vertical coordinate. Remember, the vertical coordinate of the
pen becomes the base line for drawing characters. If we set the vertical
coordinate to 0, only the descenders on the lowercase characters would be
visible in the window.
One problem with this program is that we hard-coded the font number
in the InitText procedure (we used a number instead of a symbol). Not only
is this a bad practice but we would like to know the names of the fonts we
are using. We will add a string array that defines the font name for each font
number, but first we need the following list of font names and numbers.
Font number Font name
System Font
Application Font
New York
Geneva
Monaco
Venice
London
Athens
San Francisco
Toronto
CHOIDANVAWNHO
AANA
GETTING INFORMATION ABOUT THE FONT 61
We will also define font names as constants so that when we look at
the listing of the section of our program that sets the font type, we can tell
what it’s doing. We also add a few lines of code to write the font name on
the screen below the title string (listing 4.2).
Look at the end of the main section of the program, and you will see
that we put the starting location for the pen in a pair.of variables so we can
manipulate the pen location when starting a new line of text. When we run
the program, we get the result shown in figure 4.14.
GETTING INFORMATION
ABOUT THE FONT
Looking at what the program drew, we see that the two lines of text are
quite far apart. How did we know how far down to move the pen before
drawing the second line? It was pure guesswork. We need to know how
far to move the pen between lines. The font definition in the font file has
that information, and QuickDraw has a procedure, GetFontInfo, that will
get it for us. It returns the information about the font in a record called a
FontInfo record.
type FontInfo = record
ascent : INTEGER;
descent : INTEGER;
widMax : INTEGER;
leading : INTEGER
end;
Ascent is the distance from the base line to the ascent line, the highest
point reached by any character in the font. Descent is the distance from
the base line to the descent line, the lowest point reached by a descending
portion of a character. WidMax is the maximum character width of the
characters in the font (not the maximum character image width), Leading
is the distance from the descent line of one line of characters to the ascent
line of the line of characters below it.
QuickDraw has other routines that get information about text char-
acters in a particular font. Two of them are:
function CharWidth(ch : char) : INTEGER;
CharWidth returns the width of the specified character using the
current font, font size, and style.
DRAWING TEXT
TMM Listing 4.2 DrawFont Revised
program DrawFont;
{Listing 4.2}
const
SystemFont = 0;
ApplicationFont = 1;
NewYork = 2;
Geneva 37
Monaco 4;
Venice = 5;
London 6
Athens i
SanFrancisco = 8;
Toronto = 9;
var
FontNum, StartH, StartV : INTEGER;
FontName : array[0..9] of Str255;
procedure InitDrawingWindow;
var
GraphRect : Rect;
begin
SetRect (GraphRect, 50, 50, 310, 270);
SetDrawingRect (GraphRect) ;
ShowDrawing;
end;
procedure InitText;
begin
FontName [0] "System Font';
FontName [1]
FontName [2]
"Application Font';
‘New York';
FontName [3] "Geneva'
FontName [4] "Monaco'
FontName [5] "venice'
FontName [6] "London'
FontName [7] ‘Athens';
FontName[8] "San Francisco';
FontName [9] 'Toronto';
TextFont (FontNum) 7
TextFace([]); {normal}
TextMode (srcOR) 7
TextSize (12);
end;
begin
FontNum NewYork;
InitText;
InitDrawingWindow;
StartH 10;
Startv 207
MoveTo(StartH, StartV);
DrawString('The Macintosh Character
MoveTo(StartH, StartV + 20);
DrawSt ring (FontName[FontNun] ) ;
end.
Set')7
A
A PROGRAM TO DRAW A FONT’S CHARACTER SET 63
= trawing Ss
The Macintosh Character Set
New York
(a)
Figure 4.14 The result of the revised DrawFont program
OT
function StringWidth(string : Str255) : INTEGER;
StringWidth returns the width of the specified string using the
current font, font size, and style.
Both CharWidth and StringWidth are useful when you want to see if
a character or string will fit on a line before you attempt to draw it. In the
next version of our program, we add a variable of the FontInfo type and
a call to GetFontInfo. We use the font information to calculate how far
down to move the pen before drawing the font name. (Listing 4.3 shows
just the sections that we changed.)
We did not need to define the FontInfo data type in our program
because Macintosh Pascal already has that definition as part of its Quick-
Draw data types.
A PROGRAM TO DRAW
A FONT’S CHARACTER SET
In the last version of our program, we added a section to draw the entire
character set of the font in a matrix (shown in figure 4.15). The small
rectangles are used for characters that have no image defined in the font.
Listing 4.4 shows DrawFont in final form. We add two statements to
put the title in boldface and then to return the type style to plain text. We
also add two nested FOR loops to increment the character number. We use
a DrawChar procedure to draw each individual character. Note that we
64
NT
DRAWING TEXT
Listing 4.3 DrawFont Further Revised (Variables and Main Program)
{listing 4.3, Variables and Main Program Only
var
FontNum, StartH, StartV, LineH : INTEGER;
FontName : array[0..9] of Str255;
FontStuff : FontInfo
begin
FontNum := NewYork;
InitText;
InitDrawingWindow;
GetFont Info (FontStuff) ;
StartH 10;
StartV 20;
LineH := FontStuff.ascent + FontStuff.descent
FontStuff. leading;
MoveTo(StartH, StartV);
DrawString('The Macintosh Character Set');
StartV := StartV + LineH;
MoveTo(StartH, StartV);
DrawString (FontName[FontNum]);
end.
+
IT
SSE Drawing BE al
The rk Font
eb
Maci w Y.
ry
oOoooooooOooooooooOKs
ara
Ng
&
S
Ly
a
g
Cad
@
°
r
CONVO DENK OWB
°
OAZZH AUK MAAMUIOTDE AG
oO
IMT ONN NM ESE TOMO
Wr OO $30 BB V9 BD CO: RENO DoD: Oo
EE OVO: OP OF OL IE MH ©: OO
ae - |
obDooooooooooooo0o0o0"
oOoooo0o0o0000000000"
'. £@@DHEHME & AD
we DO BOOS oe
area
+ #UA pHee
s8o°*®ooo0000*ooo00%%
AMO OW POOVAUAWNEO
OO o00 oooooooo
vo FON Mad GHuRoOUU
oS A ae OR AoOAarcgeD
oooooo%#xoo
ay: ata
Sha
BA ORD>
Figure 4.15 A character set matrix
A PROGRAM TO DRAW A FONT’S CHARACTER SET
KOON Listing 4.4 DrawFont
Program DrawFont;
{Listing 4.4}
const
SystemFont = 0;
ApplicationFont = 1;
NewYork = 2;
Geneva
Monaco
Venice
London
Athens
SanFranci
Toronto
LMargin
Offset = 3
TMargin =
var
FontNum, StartH, StartV, LineH, v, h, n,
INTEGER;
FontName : array[0..9] of str255;
FontStuff : FontInfo;
HexConv : array[0..15] of char;
Oo = 8;
0;
procedure InitDrawingWindow;
var
GraphRect : Rect;
begin
SetRect (GraphRect, 20, 40, 380, 330);
SetDrawingRect (GraphRect) ;
ShowDrawing;
end;
procedure InitText;
begin
FontName[0] := ‘System Font';
FontName[1] ‘Application Font';
FontName[2] "New York';
FontName [3] ‘Geneva!
FontName [4] "Monaco
FontName [5] "Venice
FontName [6] "London';
FontName [7] ‘Athens';
FontName[8] "San Francisco';
FontName[9] := 'Toronto';
TextFont (FontNum) ;
TextFace({]); {normal}
TextMode (srcOR) ;
TextSize(12);
end;
function HexChar (num : INTEGER) : Char;
CharWidth
Continued
66 DRAWING TEXT
KHIM Listing 4.4 Continued
begin
i£ ((num > 15) ox (num < 0)) then
HexChar := 32
else if num < 10 then
HexChar := chr(num + 48)
else
HexChar := chr(num + 55);
end;
begin
FontNum NewYork;
InitText;
InitDrawingWindow;
GetFont Info (FontStuff) ;
FontStuff.Leading := FontStuff.Leading - 1;
LineH := FontStuff.ascent + FontStuff.descent +
FontStuff. leading;
StartH LMargin - Offset;
Startv TMargin + LineH;
MoveTo(StartH, StartV);
{put the title in bold face}
TextFace ([Bold]);
DrawString('The Macintosh Character Set, ');
DrawString (FontName [FontNum] );
DrawString(' Font');
CharWidth := FontStuff.widMax + 2;
{draw the top line of hex numbers}
Startv StartV + LineH;
StartH LMargin + CharWidth;
MoveTo(StartH, StartV);
for h := 0 to 15 do
begin
DrawChar (HexChar (h) );
StartH := StartH + CharWidth;
MoveTo(StartH, StartV);
end;
{set starting location to draw characters}
Startv StartV + LineH;
StartH := LMargin;
MoveTo(StartH, StartV);
{draw the character matrix}
TextFace([])7
for v := 0 to 15 do
begin
TextFace([bold]);
DrawChar (HexChar (v) )7
StartH := StartH + CharWidth;
MoveTo(StartH, StartVv);
TextFace([])7
for h := 0 to 15 do
begin
Continued
A PROGRAM TO DRAW A FONT’S CHARACTER SET 67
Listing 4.4 Continued
DrawChar(chr(v + (h * 16)))7
StartH := StartH + FontStuff.widMax + 2;
MoveTo(StartH, StartV);
end;
StartH LMargin;
StartV := Startv + LineH;
MoveTo(StartH, StartV);
end;
end.
have had to convert the character number from an integer to the CHR data
type. Instead of using the font’s proportional spacing between characters,
we put them in a matrix so they all line up in columns and rows. This
allows us to put the hex equivalents of the character numbers across the
top and down the left side of the matrix, making it possible to locate any
character on the basis of its hex value. The HexChar function returns the
hex character (actually, its character number) for an integer that specifies
a row or column (the v and h variables).
We also have some additional code to put in the column and row
numbers (hex numbers) in boldface. It turns out that with the leading
specified in the New York font’s definition, there isn’t quite enough room
to draw the entire matrix and still be able to see the drawing window
borders at the top and bottom. Right after the call to GetFontInfo, there is
a statement to subtract 1 from the leading. Note that changing the leading
variable doesn’t affect the font definition; it’s just an internal variable that
we use to figure out how far to move the pen.
Try changing the font that the program draws to see what some of the
special characters look like in different fonts. If you choose a font that is
not installed in your system disk, QuickDraw will draw the text in the
application font (Geneva).
CANN
CHAPTER
© MORE TOOLS FOR
THE MAGICIAN
The Cursor
The Mouse
Pictures, Polygons, and Regions
Creating QuickDraw Pictures
QuickDraw Polygons
Using Regions
ANT
70
MORE TOOLS FOR THE MAGICIAN
THE CURSOR
The cursor is the image that moves around on the screen when you move
the mouse. It’s used to relate the mouse position to a point on the screen.
Most Macintosh documentation calls the cursor a pointer because its
function is to point to things on the screen. We will call it a cursor so that
we do not confuse it with a Pascal pointer data type. As you have used the
Macintosh, you have probably seen the cursor change shape depending on
what the machine is doing. When a program starts a task that takes some
time, it will change the cursor to an image of a watch to let you know that
you will have to wait. In a program like MacPaint, the cursor shape
indicates what kind of tool you are using.
In your own programs, you control the cursor with QuickDraw
procedures. You can set the cursor shape, hide the cursor, show the
cursor, or hide the cursor until the next mouse button click.
The cursor image is a 16-by-16-pixel square. As you move the cursor
around on the screen, it appears to overlay parts of the image on the
screen. When you move the cursor, the parts of the image that were
beneath it are restored.
When you define a cursor, you specify the cursor image (16 by 16
pixels), a cursor mask, and the bot spot. The cursor image is the image that
appears on the screen and follows the mouse’s movement. The cursor
mask determines which parts of the cursor image appear on the screen.
Usually, you will want the cursor mask to match the cursor image’s
outline. Thus, the pixels in the cursor image that are not part of the cursor
shape will allow the existing pixels on the screen to show through.
In figure 5.1 we see three cursors and their masks. The mask for the
left cursor covers the cursor and goes 1 pixel beyond the cursor in all
directions to create a cursor outline. When the cursor is over a white area
of the screen, it puts a black image of the arrow on the screen. When it is
over a black area of the screen, the combinat
Showing the first 100000 of 400284 characters.