Skip to main content

The Magic Of Macintosh Programming Graphics and Sound 1986

  • PublicationMac Garden Manuals
  • Pages323
  • SourceThe_Magic_Of_Macintosh_Programming_Graphics_and_Sound_1986.pdf
← Magazines The Magic Of Macintosh Programming Graphics and Sound 1986
/ 323
Page 1
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.

mp.ls