TweetFollow Us on Twitter

PicComments
Volume Number:4
Issue Number:12
Column Tag:Assembly Lab

Inside PicComments

By Raul Tabasso, Rome, Italy

Inside PicComments

Everything you’ve always wanted to know about picComments but were afraid to ask.

Raul Tabasso is currently working on his thesis in Architecture at the University of Rome where he was particularly involved in designing a CAD system. At the end of 1984 he started developing software on the Mac and since then it has become his principle activity. At the moment he is working on a large project, a Macintosh application written in assembly language that will eventually allow bitmaps (like those digitized with scanners) to be transformed into a collection of objects as in a PICT file. In his younger days Raul worked for a week as a bicycle messenger in San Francisco where he learned how to overcome slopes.

When I first read the Laser Manual and Apple Technical Notes on the subject, I was very surprised to see how easy it seemed to implement some laser specific features without the bother of sending a special PostScript (PS) program to the printer. Actually the idea of passing information to the laser driver through picture comments not only makes it very easy to include some features in your programs, but guarantees longevity and stability to your code utilizing the power of PS through QuickDraw (QD). In fact even if one day this technique could be replaced, Apple will hardly change the rules its own applications and many others depend on. This is at least if you don’t take picComments (from now on picComs) just like a trick to send your own PS programs which, although it now works, could really break the rules of device independence.

If you have experienced picComs you probably realized that, despite revisions, Apple’s own documentation surprisingly lacks precision and clearness in more than one point and that such a brilliant idea appears not to have been supported as it should. Since a lot has been already written about picComs I am not going to repeat basic information most of us already read but I will concentrate on the more obscure and less sampled aspects. For example one of the most exciting features of picComs is the ability to have easy access to PS equipped printer’s capability to smooth polygons using bezier curves. As far as I know (you can check how far in the bibliography), nothing has ever been shown about printing smoothed polygons using this scheme. Furthermore we will examine the inner working of one more ‘unrevealed’ picCom, the SetLineWidth.

• Please note that through this article we will use the term laser or laser driver for convenience where we shall actually be referring to any device and driver that can process picComs. Seemingly the term imagewriter will be referred to those devices and drivers ignoring picComs. Moreover, since there is no reason your application can’t make sure it is running on a recent driver, I cannot guarantee everything will work as described in this article using earlier versions of the 4.0 laser driver •

Figure 1. Sample Smoothed Picutres

A Different Approach

Before we get involved with smoothing, there is something to point out on sending picComs to the printing grafPort that I think is fundamental and seems to have been left out of any discussion. You probably noticed that all the examples published are structured something like this:

myPic := OpenPicture(theWorld);
 DrawStuff; {with QD + picComments}
ClosePicture;
PrintThePicture;   {go print myPic}

This will work nicely in an example test or in a small program, but in a real application will probably give you some problem. Why? Very simple. Since you usually give the freedom to your users to draw as many polygons or rotated texts as would fit in the available heap, it can easily happen that there isn’t enough memory to make a picture containing all graphics to be printed or saved.

So what to do? Can we divide the picture into smaller ones? If so we must create and kill pictures in the middle of the printing loop, and this is not allowed as specified in IM2 (p. 160) where they warn us: “Don’t call the QuickDraw function OpenPicture [ ] after a call to prOpenPage ”. Do we have to open a picture in order to use picComs? Although we usually must, when printing we do not really have to.

Let’s have a look at some facts now and see why this is possible. When we are spool printing on an imagewriter, the driver is actually writing a PICT format file on the disk (or possibly to memory). To accomplish this, it has to solve the same problem we just talked about, as nothing guarantees that it will have enough memory to accumulate the picture and eventually print it. Fortunately, it is possible to customize the standard QDprocs, the low-level bottleneck drawing routines of QD. The imagewriter driver customizes the stdPutPic procedure (in the PrOpenDoc call) in order to directly write the picture to disk while it is forming, instead of accumulating it in memory, and opens a picture in the PrOpenPage call. Since another picture is already opened you can’t open one after, but just because of that we can issue our picComs without taking the trouble (and the memory) of making one by ourselves. The standard commentProc for the print port of the imagewriter will just ignore comments anyway.

I know you are wondering what if we are printing on a laser, since it usually prints in a draft mode. Ok, we don’t have a picture opened and we are not writing a PICT to the disk, but the driver has customized the commentProc that, as you guessed, is in charge of processing picComs causing them to be directly intercepted and sent to the laser without being accumulated anywhere.

All this simply means that you can safely issue picComs in your page loop without having to pass through the construction of a picture, this allowing you to print everything you want without worrying about memory problems besides making your print job faster and easier.

So our previous routine should look something like this:

PrOpenPage(MyPrintPort,NIL);{don’t need to open a picture}
 DrawStuff;  {using QD + PicComments}
PrClosePage(MyPrintPort);  {end cur page}

Smooth Commenting

When we have to smooth polygons we usually write a routine that draws many short line segments shaping the curve. We can use it to draw on the screen or in the printing grafPort of the imagewriter, but no matter how good our smoothing algorithm is, when we attempt to send it to a laser printer the results are far from being attractive. This is because the smooth-poly will always be printed at a 72 dpi resolution, making your hard copy much less impressive than it could be. This, of course, is before using picComs for your poly (sorry if it sounds like an ad).

Using picComs is not only useful for printing but very recommendable for saving (in a file or in the scrap) a PICT containing objects that can be better explained adding comments. Doing it in this way will allow other applications to reconstruct the objects in the right format. To simplify this, imagine that you have just saved in a picture file the short line segments forming the smooth-poly. An application like Mac Draw does not know that you do not want a very fractal object or just a number of connected lines, thus treating your masterpiece like a senseless doodle. Using picComs you can tell MacDraw that those short lines belong to a smoothed polygon.

Although the whole mechanism is pretty easy (once you know it) there are some rules to follow. The final goal of our discussion is to write a routine that is not concerned with the printer type but that takes advantage of specific situations without risk of being device dependent. This routine could also be called in order to save (on disk) or to copy a picture (in the scrap). In fact it is indifferent to send our picComs and drawings to the printer, to the disk or to the memory.

It is important to notice that some applications, like MacDraw (and our demo), do not always use a standard QD polygon (a 10 bytes header plus 4 bytes for each point), but a compressed version. When several polygons with many sides are in memory, the use of compressed polys could save a lot of memory. If, for example, you use a byte offset format nearly half the space can be saved. It is not convenient then to convert this object to a standard Poly in order to draw it using stdPoly; in fact it will be more trouble and take more code and memory than if you simply drew it using stdLines. Subsequent calls to Lineto or Line, after the PolyBegin comment, will be interpreted as part of the poly.

Let’s now have a closer look at this routine. We always start the poly definition with a PolyBegin comment. This informs a driver, prepared to understand picComs, that the following stdLines (or stdPoly) are to be considered as part of a special polygon. Immediately following this comment we might issue the PicPlyClo comment which specifies that the polygon is a closed figure.

This header is the same for smoothed or unsmoothed polys, but at this point we should differentiate our code. If the current poly is not to be smoothed we can just draw it normally which would work for every printer; but if it does have to be smoothed we should issue additional information which is done through another picCom, the polySmooth. This comment takes a handle to one byte of data specifying the nature of the splined poly: the verbs parameters. These verbs provide a way to apply framing, filling and closing to the current object.

Right after this last comment we should draw the unsmoothed poly. Notice that we draw the original poly even though we want the smoothed one, since the laser only cares about the original vertexes on which will be applied its own smoothing algorithm (a cubic spline, if you care). If you are wondering, what if an imagewriter gets these vertexes instead of the smoothed ones, you have probably guessed that it would print the wrong thing, so we have to find a way not to have it print on paper. The official Apple documentation suggests setting the pen size to zero after the polyBegin comment. Unfortunately this method doesn’t work properly. It does avoid printing the unsmoothed poly on a printer that is not equipped to handle polygon comments but, too bad, even on the laser the pen remains at zero and nothing will print.

The real solution has to do with clipping. What we have to do is simply set the clipping to a zero rect (a rectangle enclosing no pixels) before drawing the unsmoothed poly and then reset it to the previous clipping when finished. This will prevent an imagewriter from drawing the wrong thing, but will allow a laser, that ignores the clipping rectangle (when in a special poly definition), to draw the smoothed polygon.

• It is interesting to note that a picture with the clipRgn set to an empty rect does not generate any opcode for this operation since such a clipping is actually its default •

At this point we are done with the laser, but nothing would have been printed yet on an imagewriter, so we must get ready to call our own smoothing algorithm. However, first we must ensure that the laser won’t draw again. This is very easily done by the polyIgnore comment that simply puts the laser driver to sleep (actually only drowsing) until a polyEnd is encountered. Is that all? Basically this is it, but there are a few other things we should examine to completely master the whole scheme.

If the polygon has to be filled, as well as using the fill verb in the PolySmooth comment, the QD procedure fillRgn can be used. This comes after the PolyIgnore and will work for every printer. In fact the laser driver, even though it ignores regions, will be awakened from its sleep and will build a fillPat for its current path using that passed to the FillRgn proc. Some of the patterns have been optimized in order to translate them in a gray-scale that PS can halftone. The actual patterns that the driver transforms in a gray-scale, are the standard fill tones present in the system file (and as QD globals) plus some of the patterns in the standard list of the system file: to be exact those with numbers 1 to 5 (2=5) and 20 to 24 (23=24). All these patterns can be found as resources (PAT# ,ID=-8191) in the laser driver where, two more pats in the list (8 and 18), raise the total available shades of gray to the number of 11 including black and white. You can, therefore, easily put up a pattern menu with more real PS gray scales than most applications without having to deal with special coding or, in the worst case, having to write a driver yourself. In addition these patterns are much faster than others because they don’t need to be imaged before being sent to the laser. Using these pats you can have gray-scales from white to black at approximately 10% increments in gray percentage. Currently there is a gap between 50 and 75 percent and there are 3 gray levels between 75% and 82% all very similar, so let me put on the wish list a gray around 60% instead of one of these last 3.

Let’s summarize all this with a short pseudo-language example assuming our polygon is stored in a byte offset format, therefore using Line or LineTo procedures in its drawing routines. Shouldn’t be hard to adapt this example in order to use normal QD polys.

PrOpenPage(MyPrintPort,NIL);  {check err}
 {Begin special poly}
 PicComment(PolyBegin,0,NIL);
 PicComment(picPlyClo,0,NIL);
 IF smoothed THEN BEGIN  {smoothPoly}
 {verb is an unsigned byte}
 IF noFill THEN SmoothHdl^^.verb=5;
 {close+frame+fill (4+1+2)}
 ELSE SmoothHdl^^.verb=7;
 {set smooth flag and verb}
 PicComment(Polysmooth,1,SmoothHdl);
 GetClip(saveClip);
 {laser don’t care of Clip}
 ClipRect(ZeroRect); {but others do}
 {feed laser with original vertexes}
 DrawUnsmoothedPoly;
 SetClip(saveClip);  {restore}
 {laser can take a nap now}
 PicComment(PolyIgnore,0,NIL);
 END;  {smoothPoly}
 IF filled THEN BEGIN{make a rgn to fill our special poly} 
 OpenRgn; {assuming we have a handle}
 DrawCurPoly;  {create region}
 CloseRgn(RgnHandle);
 FillRgn(RgnHandle,itsPat);
 END;
 DrawCurPoly;  {redraw}
 PicComment(PolyEnd,0,NIL);
 {End special poly}
PrClosePage(MyPrintPort); {end cur page}
.....................
PROCEDURE DrawCurPoly;
 {decide what routine to call}IF smoothed THEN DrawSmoothedPoly;
 ELSE DrawUnsmoothedPoly;
END;

Les jeux sont fait; this routine will print on any kind of printer regardless of whether it knows how to interpret picComs or if the current poly is smoothed or unsmoothed. The scheme represented in this routine is similar to that used by MacDraw and this guarantees that it will be supported for a long time.

As we said, besides to print, we can use this routine to store in the scrap or to save a PICT file on the disk and we can even use it to draw on the screen (although it’s slower than normally). When we want to save a picture in the scrap we open a picture and then call our routine causing drawings and picComs to be accumulated. In order to inform an application receiving our PICT that it contains comments, we should bracket the special poly in two additional comments: picDwgBegin and picDwgEnd. Although these comments were designed for MacDraw, some other application could count on them, so even if they might be useless we should include them.

Let’s now see a slightly different way to accomplish the same task which is derived from the new scheme used in MacDraw II. Basically it doesn’t differ much from its predecessor except for the method used to fill the poly. As we recall from the previous example a region was build to fill the pseudo-poly whether it was smoothed or not, but, as we’ll see now, a standard QD polygon can be used for the same purpose.

Using a QD poly, of course, involves building one. Keeping track of its size is a much more easier thing to do than for regions. In fact if you inconsiderately trespass the 32K limit of regions and polygons some really undesirable things will happen soon. How too make sure this won’t happen using a region is (too bad) beyond the scope of this article but, as you probably guessed, checking for this kind of error while a polygon is forming is a much easier task: just make sure you don’t have more then 8189 points or, using GetHandleSize, check for the 32K limit not to be passed. The original MacDraw ignores a fill request for a polygon that does not fit in a 32K region. This means that if you try to fill a big and complicated smoothed poly MacDraw refuses to do it. Surprisingly MacDraw II doesn’t take advantage of a much easier size checking derived by using polygons and dies in a horrible way when overloaded.

Here is how it could look like our previous routine modified in order to use the procedure FillPoly instead of FillRgn:

PrOpenPage(MyPrintPort,NIL); {check err}
 PicComment(PolyBegin,0,NIL);
 IF Fill THEN BEGIN  {build Poly}
 ThePoly := OpenPoly;
 DrawCurPoly; {accumulate lines}
 ClosePoly;
 END;  {build Poly}
 IF smoothed THEN BEGIN  {smoothPoly}
 IF noFill THEN SmoothHdl^^.verb=5;
 ELSE SmoothHdl^^.verb=7;
 PicComment(Polysmooth,1,SmoothHdl);
 END;  {smoothPoly}
 IF Fill THEN FillPoly(ThePoly,ThePat);
 GetClip(saveClip);
 ClipRect(ZeroRect); {laser ignores this}
 DrawUnsmoothedPoly; {original vertex}
 SetClip(saveClip);  {restore}
 PicComment(PolyIgnore,0,NIL);
 DrawCurPoly;  {draw poly frame}
 PicComment(PolyEnd,0,NIL);
PrClosePage(MyPrintPort); {end cur page}

Notice that, as shown in the other example, the procedure DrawCurPoly checks whether the poly is smoothed and, if this is so, calls the smoothing algorithm, otherwise draws the lines shaping the poly using original vertexes. When we call DrawCurPoly right after the PolyIgnore picCom it might also be possible to issue a FramePoly QD proc instead. In this case nothing guarantees you were able to build one since it might have happened that the poly exceeded the size limit making its creation impossible. Using stdLines always guarantees you will be able to frame your object.

MacDraw II actually does a few other things than in this example. Those things seem completely useless to me, so I took them out of the example test that demonstrated to work well even without those cryptic statements. In fact MacDraw II would start moving the pen out of the way (-8000,-8000) and then setting the pensize to zero, however, after the FillPoly proc (before any intervening drawing), it would reset the pensize and location cancelling the previous operation. Mac Draw II uses a zero region to fill the poly (as we discussed before). The laser driver is not concerned with the region data but uses only the fill pattern passed along with the procedure. This, again, is useless since the driver would have already taken the pattern at the issue of the FillPoly proc in order to fill its current path. Note that MacDraw II passes a 2 bytes verb in the PolySmooth picCom instead of the only one used originally. However this second byte seems not to be used and cleared to zero at the moment.

Although there might be some invisible reason (at least to me) for MacDraw II to use this additional statements, you can safely use the slimmed version we just saw or the scheme used by the original MacDraw shown before ( and also presented in the demo). Both of These methods have demonstrated to work fluently in all the tests I produced. They also worked fine in order to save a Pict file format on disk. In this case we must write a file having cleared to zero its first 512 bytes block (usually containing MacDraw specific information) followed by our picture. As we said for the imagewriter driver, we may not have enough memory to accumulate the picture in memory before writing it to disk, so we should customize the stdPutPic in order to directly spool it to disk while it is forming.

Real Width

Let’s now have a look to another interesting picCom which has the ability to scale the pen size even under the smallest unit (1/72 of an inch) that QD can represent. We are talking about the SetLineWidth (from now on SLW) comment that allows you to draw with an almost infinitely thin pen, taking advantage of higher resolution laser printers.

This is a very important comment that, I think, every application dealing with graphics should use in order to give its users the possibility to fully utilize laser capabilities.

SLW takes a handle to a 4 bytes data point which is actually to be interpreted as a real number. The first high order integer (2 bytes) is the numerator and the second the denominator of a fraction giving the scaling factor for the current pen. For example if you pass 1 for the numerator and 4 for the denominator you will have a value of 0.25 (1/4) that will be used to determine the current pen for the SetLineWidth PS operator.

• Don’t confuse the SetLineWidth picCom with the homonymous PS operator which actually assigns the pensize for the laser •

The LaserPrep file contains variables and procedures that the driver downloads to the printer. The SLW picCom works by changing the value of some of these variables. There is a global variable flag string (fg) containing single-character values; the fourth and the fifth parameters of this string represent respectively the numerator and the denominator of a variable named pnm. The SLW picCom stores the two parameters it receives in these character bytes therefore limiting to an unsigned byte (0-255) the maximum possible value that can be passed. Note that you pass numbers in integer format but they are interpreted as unsigned bytes. If for example you wish to scale your pen to 300 dpi (instead of the 72 default), it would be fatal to issue a SLW having 72 in the first word and 300 in the second ($ 0048 012C). In fact, the number 300 will not fit in the unsigned byte denominator of pnm which would cause the driver to print nothing or to bomb totally (depending on version). To perform such an operation, scale the number to a common factor like 12 for example, giving a fraction of 6/25 which will scale the pen exactly like 72/300 would have done.

Unfortunately the SLW picCom is very tricky and often doesn’t appear to work as expected until you know its idiosyncrasies. Let’s see the inner workings of this comment in conjunction with the laser driver.

• For simplicity we’ll consider width and height of QD pen as a unique parameter •

Any time you issue a SLW picCom, in the printing loop, the laser driver translates it into a call to the previously downloaded lw (line width) procedure. The lw proc calculates the width of the pen that is used for the SetLineWidth PS operator. If we name ‘LineWidth’ the variable containing the thickness of lines to be drawn (what an imagination) and ‘pnmV’ and ‘pnmH’ are the two parameters you pass in the SLW picCom, the lw procedure works about like this: LineWidth = LineWidth * (pnmV/pnmH). Then the SetLineWidth PostScript operator will take LineWidth as its argument setting the pen for the laser to this new value. This means that every new scaling factor you pass through the SLW comment will not actually act on the current QD pensize, but will multiply the PS pen. For example if you have a starting QD pensize of 2 and you pass a pnm of 3/2 (1.5) the PS pen will be set to 3 and this is no surprise, but if a further SLW is generated with a pnm of 1/2 (0.5) we will have a new pen of 1.5 (3 * 0.5) and not 1 as would have resulted multiplying the QD pen (2) with the new scaling (0.5). So one more SLW comment with a pnm=0.5 will set the PS pen to 0.75 leaving unchanged the QD pen (2).

Further more we must not forget that even the pensize QD procedure affects the laser pen width, but differs from the SLW picCom, in that it is not directly processed and sent to the laser. In fact until a new drawing routine is called, arguments passed to pensize are only recorded but no action is taken. When a QD bottleneck is called the driver checks the last value passed to pensize and, if it’s different from the previously used value, it generates a ‘pen’ LaserPrep’s routine. Imagining the new QD pensize to be a unique value (for width and height) named NewSize this procedure would look about like this: LineWidth = NewSize * (pnmV/ pnmH). LineWidth will be then passed to the SetLineWidth PS operator.

This procedure will have the effect of resetting the laser pen according to the new QD pen and the current pnm. So, considering we left our PS pen to 0.75 and the pnm to 0.5 as in the previous example, making a call to PenSize with an argument of 1 would cause the pen (when a QD proc is called) to be set at 0.5, indeed NewSize * pnm (1 * 0.5).

Notice that, because the laserPrep’s ‘pen’ procedure is called right before a QD routine (if at all), it is always issued after the ‘lw’ procedure even if the original order was the opposite.

So many things just to set a pen width? Don’t ask me, but once you figure out the mechanism it shouldn’t be impossible to make it work. I suggest that, following the rules we have just talked about, you write a short routine in your favorite language to test all the quirks of this comment.

Here’s a pseudo one that can serve as a skeleton and assumes (for simplicity) the QD pen to be a unique value:

LineWidth = Float{PS pen}
QDPenSize = Integer{QD cur pen size}
pnm= Float{pen multiplier}
NewSize = Integer{last val of PenSize}
...................
{Everytime a SLW picCom is issued call the followin proc to simulate 
the driver}
PROCEDURE Lw (pnmH,pnmV);
 pnm := pnmV / pnmH;
 LineWidth := LineWidth * pnm;
END;  {Lw}
...................
{If a QD PenSize is called just set NewSize accordingly}
{Before drawing, check IF QDPenSize <> NewSize THEN (in case) call this 
routine}
PROCEDURE Pen;
  QDPenSize := NewSize;
 LineWidth := QDPenSize * pnm;
END;  {Pen}
...................
{You can then print LineWidth, instead
drawing, to test the PS pen size }

About the Demo

The principle goal of our Demo is to show how to correctly insert in a working program the routines we have been talking about.

The program allows a polygon to be drawn (in a non modal way) in a standard Macintosh window, you can then take some action on it through menus. It is possible to smooth and vice-versa, fill the poly with some patterns and change the pen size. Moreover you can copy the current poly in the clipboard and, because desk accessories are supported, you can then paste it in to those of them accepting picts or in other applications. Of course you can print the current poly on any printer you wish and setting the pen scaling factor for PostScript printers will allow a much more accurate definition for the pen width like drawing hairlines as thin as possible on the currently attached device. For example a pen size of 3 with a scaling factor of 300 dpi will cause lines to be printed at a width of 1/100 of an inch (3 * 1/300) which on a laser writer’s raster memory will be exactly 3 pixels.

Although the program only serves as a demo its routines have been designed to be fast and compressed in order to be perfectly usable in a larger project. For example the actual procedure in charge of smoothing an input poly can be considered like a Toolbox routine for its speed and compression. You could even be disinterested in how this routine works (of which a full discussion would go beyond the scope of this article), and you could just call it from your program passing the right arguments like you would do with any Toolbox proc. Seemingly the routine that builds a special polygon (‘MakePoly’) can be easily used from a different application even if dealing with hundreds of smoothed polys instead of the only one we can have in the demo. Points where potential problems may arise have been marked and a short description of the possible bug is given. This, of course, is done not for carelessness but because a lot of code would have been needed and we couldn’t take up any more space. Assembly language has been chosen mainly for its insuperable speed since we all experienced what it means in terms of time to scroll a certain number of smoothed polygons in a window. Furthermore having to deal with compressed data structures makes it much easier to control them in this language. Macros have been used for comments dividing them in short and long as in the picture’s opcodes. Routines that refer more closely to the subject of this article, as you have seen, have already been shown in a pseudo high-level language. Hierarchical menus have been used where appropriate and the new PrGlue Toolbox trap (in 256K ROMs and in system 4.1 or later) is used for printing code.

I hope this discussion saved you some time in implementing comments in your applications. Please note that despite most of the information being presented in this article is not documented anywhere else, it is not a weird approach to accomplish the work done but just the way things are at the moment and probably will be for a while.

Besides the following documentation MacsBug was of invaluable help as usual.

Bibliography

Inside Macintosh™, Addison-Wesley Publishing Company Inc. 1985-88, Vol.I, chap. 6, p.159, 189; Vol.II, chap. 5; Vol.V chap. 4, pp.85-105

APDA, Apple Laser Writer Reference Manual, Apple Computer Inc., Cupertino CA 1987

APDA, Macintosh Technical Notes 1988, notes: #21, #27, #72, #91, #122, #175, #181

Adobe Systems Inc., PostScript Language Reference Manual, Addison-Wesley Publishing Company Inc. 1985, 7th Printing 1987

Adobe Systems Inc., PostScript Language Tutorial and Cookbook, Addison-Wesley Publishing Company Inc. 1985, 7th Printing 1987

Adobe Systems Inc., PostScript Language Program Design, Addison-Wesley Publishing Company Inc. 1988

Dan Weston, The Complete Book of Macintosh Assembly Language Programming, Scott, Foresman & C., Glenview, Illinois London 1987, Vol.II, chap. 4.

Scott Knaster, Macintosh Programming Secrets, Addisoison-Wesley Publishing Company Inc. 1988, 2nd Printing, chap. 6

Dave Kelly, David E. Smith, Pascal Procedures. MultiFinder Friendly MacDraw Plotter, «MacTutor», vol.4, n.2, February 1988, pp.16-41

Joel West, Comments About Picts, «MacTutor», vol.4, n.6, June 1988, pp.40-48

{1}
Listing:  Smooth.Link
/Output Smoother

/Type ‘APPL’ ‘DEMO’
/Bundle
Smooth

/Resources
SmoothRes

/End
{2}
Listing:  Smooth.asm

; ----------------------------------------------------------
; - Smoother: picCommenting polygons & non solo ------------
; - Raul Tabasso - via delle Isole 23/A - 00198 ROMA ------
; ------------------------------------------------------------
; WARNING: because this program is intended as a demo, there 
;   are some circumstances where a stronger error checking 
;   should be applied. Due to this, situations where a 
; potential problem may arice,
;  are marked with a (¿) symbol and a short advice is given.
; ------------------------------------------------------------
Include Inside comments:SmoothSource:Traps.D
; ----------------------------------------------
MBarHeightEQU  $BAA;GENERAL EQUATES
DoubleTimeEQU  $2F0
screenBitsEQU  $FF86
PortRectEQU 16
; ----------------------------------
PrGlue  EQU $A8FD;New print manager call
PrOpen  EQU $C8000000;PrGlue’s routine selectors
PrClose EQU $D0000000
PrintDefaultEQU  $20040480
PrStlDialog EQU  $2A040484
PrJobDialog EQU  $32040488
PrOpenDoc EQU  $04000C00
PrCloseDocEQU  $08000484
PrOpenPageEQU  $10000808
PrClosePage EQU  $1800040C
PrPicFile EQU  $60051480
PrError EQU $BA000000
; ----------------------------------
iCopies EQU 4    ;Print Manager Stuff
prInfo  EQU 2
prJob   EQU 62
rPage   EQU 6
bJDocLoop EQU  6
; ----------------------------------
PicDwgBeg EQU  130 ;PicComment Kinds
PicDwgEnd EQU  131
PolyBegin EQU  160
PolyEnd EQU 161
PolyIgnoreEQU  163
PolySmoothEQU  164
PicPlyClo EQU  165
SetLineWidthEQU  182
; ----------------------------------
Frame   EQU 1    ;PolyVerbs
Fill    EQU 2
Close   EQU 4
; =======================================
; ------------  M A C R O  --------------
; --------------------------------------
MACRO prExecRoutineSelect =
 MOVE.L #{RoutineSelect},-(SP);routine selector on stack
 DC.W PrGlue;execute trap
 |
; --------------------------------------
MACRO ShortComment Kind =
 MOVE.W #{Kind},-(SP)
 CLR.W  -(SP)
 CLR.L  -(SP)
 _PicComment
 |
; --------------------------------------
MACRO LongCommentKind,Size,Hand  =
 MOVE.W #{Kind},-(SP)
 MOVE.W #{Size},-(SP)
 MOVE.L {Hand},-(SP)
 _PicComment
 |
; --------------------------------------
MACRO CheckItm MHandle,Item =
 MOVE.L {MHandle}(A5),-(SP)
 MOVE.W {Item},-(SP)
 ST-(SP);check
 _CheckItem
 |
; --------------------------------------
MACRO UnChkItm MHandle,Item =
 MOVE.L {MHandle}(A5),-(SP)
 MOVE.W {Item},-(SP)
 CLR  -(SP) ;uncheck
 _CheckItem
 |
; =======================================
; -------- Register usage --------------
PolyHandEQU A4
; ------------------------------------
 BSR  InitGlobals;Do not rearrange the
 BSR  InitMac    ;order of these subs
 BSR  GetHandles
 BSR  SetMenus
 BSR  SetWindow
 BRA  NewPoly    ;init one
; ======================================
Loop  _SystemTask
 CLR.W  -(SP)
 MOVE.W #$FFFF,-(SP) ;Every event
 PEA  EVENTRECORD(A5)
 _GetNextEvent
 MOVE.B (SP)+,D0
 BNE.S  DoEvent
 TST.W  whileDrawing(A5)  ;Check if we have to 
 BNE.S  TrackLine;Idle line
 BRA.S  Loop

DoEvent MOVE.W WHAT(A5),D0
 CMP.W  #1,D0    ;is mousedown ? 
 BEQ  MouseDown
 CMP.W  #3,D0    ;is keydown ? 
 BEQ  KeyDown
 CMP.W  #6,D0    ;is update ?
 BEQ  UpDate
 CMP.W  #8,D0    ;is activate ?
 BEQ  DoAct 
 BRA.S  Loop;we don’t support other events
; ==========================================
TrackLine
 PEA  CurPoint(A5)
 _GetMouse
 MOVE.L CurPoint(A5),D0
 CMP.L  OldPoint(A5),D0 ;Did we move since last time ?
 BEQ.S  Loop
 ;we are in XOR mode
 MOVE.L LastPoint(A5),-(SP) ;start Pt
 _MoveTo
 MOVE.L OldPoint(A5),-(SP);overwrite old line
 _LineTo
 MOVE.L LastPoint(A5),-(SP) ;draw new line
 _MoveTo
 MOVE.L CurPoint(A5),-(SP)
 _LineTo
 MOVE.L CurPoint(A5),OldPoint(A5)  ;c’est la vie
 BRA.S  Loop
; ------------------------------------------
DoAct MOVE.WMODIFY(A5),D0
 BTST #0,D0 ;Activate or deactivate
 BEQ.S  DeAct
 MOVE.L MESSAGE(A5),-(SP)
 _SetPort
 BSR  DimMenu    ;we only support copy menu
 BSR  PutGrow
 BRA  LOOP
DeAct BSR ActMenu;Allow complete edit menu for DA
 PEA  GrowBox(A5)
 _EraseRect ;clean it and
 BSR  PutGrow    ;draw inactive
 BRA  LOOP
; ----------------------------
UpDate
 MOVE.L TheWind(A5),-(SP)
 _BeginUpdate
 TST.W  PolyDrawn(A5)
 BEQ.S  @0;branch if no poly
 BSR  ReDraw;show poly
 BRA.S  @1
@0 TST.WWhileDrawing(A5)
 BEQ.S  @1;Exit if we are not either drawing
 BSR  DrawPoly   ;Redraw part already drawn
 MOVE.L LastPoint(A5),-(SP) ;redraw idle line      _MoveTo
 MOVE.L OldPoint(A5),-(SP)
 _LineTo
@1 BSR  PutGrow
 MOVE.L TheWind(A5),-(SP)
 _EndUpdate
 BRA  LOOP
; ------------------------------
MouseDown
 CLR.W  -(SP)
 MOVE.L WHERE(A5),-(SP)
 PEA  WINDOW(A5)
 _FindWindow
 MOVE.W (SP)+,D0
 MOVE.W D0,D3    ;needed for zoom box
 ADD.W  D0,D0
 MOVE.W WTABLE(PC,D0.W),D0
 JMP  WTABLE(PC,D0.W)
WTABLE  DC.WLOOP-WTABLE
 DC.W DoMenu-WTABLE
 DC.W SysEvnt-WTABLE
 DC.W DoContent-WTABLE
 DC.W DoDrag-WTABLE
 DC.W DoGrow-WTABLE
 DC.W Loop-WTABLE
 DC.W DoZoom-WTABLE
 DC.W DoZoom-WTABLE
; --------------------------------
KeyDown CLR.L  -(SP)
 MOVE.W MESSAGE+2(A5),-(SP)
 _MenuKey
 MOVE.L (SP)+,MENU(A5)
 BRA.S  ExMenu
; --------------------------------
DoMenu
 CLR.L  -(SP)
 MOVE.L WHERE(A5),-(SP)
 _MenuSelect
 MOVE.L (SP)+,MENU(A5)
ExMenu  CLR.W  -(SP)
 _HiliteMenu;Hilite off
 MOVE.W MENU(A5),D0
 CMP.W  #4,D0    ;is a submenu ?
 BLE.S  @0
 SUB.W  #’A’-4-1,D0;Allign Submenu item-60 (65-4-1)
@0 ADD.WD0,D0
 MOVE.W MTABLE(PC,D0.W),D0
 JMP  MTABLE(PC,D0.W)
MTABLE  DC.WLOOP-MTABLE   ;Menus
 DC.W DoMela-MTABLE
 DC.W DoFile-MTABLE
 DC.W DoEdit-MTABLE
 DC.W DoDo-MTABLE
 DC.W DoPensize-MTABLE    ;submenus
 DC.W DoPat-MTABLE
 DC.W DoPenscale-MTABLE
; ----------------------------
DoContent
 CLR.L  -(SP)
 _FrontWindow
 MOVE.L (SP)+,D0
 CMP.L  WINDOW(A5),D0
 BEQ.S  @0;Already in front
 MOVE.L WINDOW(A5),-(SP)
 _SelectWindow
@0 BSR  ChkDoubClick ;set DoubleClick(A5) accordingly
 MOVE.L WHERE(A5),MousLoc(A5)
 PEA  MousLoc(A5)
 _GlobalToLocal
 
 TST.W  PolyDrawn(A5);do we have a poly yet ?
 BNE  Loop;yes, no action implemented
 TST.W  whileDrawing(A5)  ;No, is it initialized yet ?
 BNE  AddPoint   ;yes, one more line for the poly
 BRA  PolyInit   ;No, start a new one
; ----------------------------------------
DoDrag  MOVE.L WINDOW(A5),-(SP)
 MOVE.L WHERE(A5),-(SP)
 PEA  WBounds(A5)
 _DragWindow
 BRA  LOOP
; --------------------------------------
DoGrow  CLR.L  -(SP)
 MOVE.L WINDOW(A5),-(SP)
 MOVE.L WHERE(A5),-(SP)
 PEA  WSize(A5)
 _GrowWindow
 MOVE.L (SP)+,D0
 BEQ  LOOP;Laizy, if not grown

 MOVE.L WINDOW(A5),-(SP)
 MOVE.L D0,-(SP)
 ST-(SP)
 _SizeWindow
 PEA  GrowBox(A5)
 _EraseRect ;Erase it and
 PEA  GrowBox(A5);put it in the update rgn
 _InvalRect 
 
ResetWndBSR CalcGrow
 BSR  ClipWind
 BSR  PutGrow
 BRA  LOOP
; ------------------------------------
DoZoom  CLR.W  -(SP)
 MOVE.L WINDOW(A5),-(SP)
 MOVE.L WHERE(A5),-(SP)
 MOVE.W D3,-(SP) ;PartCode in or out
 _TrackBox
 TST.W  (SP)+
 BEQ  LOOP
 MOVE.L WINDOW(A5),A0
 PEA  PortRect(A0)
 _EraseRect
 CLR.W  -(SP)
 MOVE.L WINDOW(A5),-(SP)
 MOVE.W D3,-(SP) ;PartCode in or out
 SF-(SP)
 _ZoomWindow
 BRA  ResetWnd
; ----------------------------------------
SysEvnt PEA EVENTRECORD(A5);make system happy
 MOVE.L WINDOW(A5),-(SP)
 _SystemClick
 BRA  LOOP
; ----------------------------------------
PolyInit
 MOVE.L MousLoc(A5),-(SP) ;Start poly
 _Moveto
 MOVE.W #10,-(SP);XOR mode while drawing poly
 _PenMode
 MOVE.L #$10001,-(SP)
 _PenSize ;Pen 1,1
 MOVE.W #1,NumPoints(A5)  ;First point
 MOVE.L (PolyHand),A0
 MOVE.L MousLoc(A5),(A0)  ;write it in the list
 MOVE.W #4,BytesWritten(A5)
 STwhileDrawing(A5);follow mouse movement with a line
 MOVE.L MousLoc(A5),LastPoint(A5)  ;init points
 MOVE.L MousLoc(A5),OldPoint(A5)
 BRA  Loop
; ----------------------------------------
AddPoint
 TST.W  DoubleClick(A5) ;user wants to close the poly ?
 BNE  ClosePoly
 MOVE.L LastPoint(A5),-(SP) ;clear last line
 _MoveTo
 MOVE.L OldPoint(A5),-(SP)
 _LineTo
 _PenNormal ;Reset pen
 MOVE.L LastPoint(A5),-(SP) ;add new line
 _MoveTo
 MOVE.L MousLoc(A5),-(SP)
 _LineTo
 MOVE.W #10,-(SP);put back XOR mode
 _PenMode
 BSR  DoAdd ;put new point into the list
 CMP.W  #32760,BytesWritten(A5)  ;Do not write past block
 BPL  ClosePoly  ;force to close in case
 BRA  Loop
; ------------------------------------------
; Pack ‘MousLoc’ into compressed poly data list :
; A negative word signals a short delta; a word as follows 
; / bit 15 always set / bits 14-8 = dy (-64,63) / bits 7-Ø = dx 
; (-128,127)/ A positive word signals a 4 bytes point (Y & X) 
; as usual WARNING(¿): this scheme won’t work if high bit of 
; point is negative(if a Y becomes <Ø a point will be 
; interpreted as a short offset)
; ------------------------------------------
DoAdd ADDQ.W#1,NumPoints(A5);one more point
 MOVE.L (PolyHand),A0;Pointer in A0
 ADDA.W BytesWritten(A5),A0 ;get to mark

 MOVE.W MousLoc(A5),D0    ;current Y
 SUB.W  LastPoint(A5),D0  ;D0 = Delta Y
 CMP.W  #63,D0   ;Dy must be in the range
 BGT.S  LongPoint;doesn’t fit
 CMP.W  #-64,D0
 BMI.S  LongPoint
 ;Y fits, now check X
 MOVE.W MousLoc+2(A5),D1  ;current X
 SUB.W  LastPoint+2(A5),D1;D1 = Delta X
 CMP.W  #127,D1  ;Dy in a signed byte range ?
 BGT.S  LongPoint
 CMP.W  #-128,D1
 BMI.S  LongPoint
WordPoint ;Word format Point (compressed)
 ADD.W  #2,BytesWritten(A5) ;only 2 bytes for a short offset
 BSET #7,D0 ;force a negative word to signal
 MOVE.B D0,(A0)+ ;write Y offset
 MOVE.B D1,(A0)  ;write X offset
 BRA.S  AddDone
LongPoint ;Long format (normal)
 ADD.W  #4,BytesWritten(A5) ;not an offset, a Point
 MOVE.L MousLoc(A5),(A0)  ;always positive in our plane
AddDone 
 MOVE.L MousLoc(A5),LastPoint(A5)  ;update
 MOVE.L MousLoc(A5),OldPoint(A5)
 RTS
; ------------------------
ClosePoly
 STPolyDrawn(A5) ;Poly is now completed
 CLR.W  whileDrawing(A5)  ;we are through
 _PenNormal ;reset mode and size
 MOVE.L DoHand(A5),-(SP)
 MOVE.W #1,-(SP)
 _EnableItem;allow unsmooth/smooth
 MOVE.L FileHand(A5),-(SP)
 MOVE.W #3,-(SP)
 _EnableItem;and print
 
 MOVE.L (PolyHand),A0;Pointer in A0
 MOVE.L (A0),MousLoc(A5);save extra code simulating a click
 BSR  DoAdd ;at last point (MousLoc) = first
 _OpenRgn ;WARNING :(¿) should make sure our poly is not 
 BSR  DrawPoly   ;too big to fit in a region.
 MOVE.L RgnHand(A5),-(SP) ;if it doesn’t fit, we are dead
 _CloseRgn
 BSR  SetPen
 BSR  CleanWind
 BSR  ReDraw;show poly
 BRA  Loop
; =============================
DoMela
 CMPI.W #2,MENU+2(A5)
 BGT  @1
 ;Show about dialog
 CLR.L  -(SP)    ;space for result
 MOVE.W #256,-(SP) ;ID
 CLR.L  -(SP)    ;NIL storage
 MOVE.L #-1,-(SP);in front
 _GetNewDialog
 MOVE.L (SP)+,A2
@0 CLR.L-(SP)
 PEA  ItmHit(A5)
 _ModalDialog
 CMP.W  #1,ITMHIT(A5)
 BNE.S  @0
 MOVE.L A2,-(SP)
 _DisposDialog
 BRA  LOOP
 
@1 MOVE.L MelaHand(A5),-(SP);Get DA
 MOVE.W MENU+2(A5),-(SP)
 PEA  DskName(A5)
 _GetItem
 CLR.W  -(SP)
 PEA  DskName(A5)
 _OpenDeskAcc
 MOVE.W (SP)+,D0
 BRA  Loop
; --------------------------------
DoFile
 MOVE.W MENU+2(A5),D0;Item Num
 ADD.W  D0,D0
 MOVE.W M1TAB(PC,D0.W),D0
 JMP  M1TAB(PC,D0.W)
M1TAB DC.WLOOP-M1TAB
 DC.W NewPoly-M1TAB
 DC.W PageSetup-M1TAB
 DC.W Print-M1TAB
 DC.W LOOP-M1TAB ;Laser Pen Menu
 DC.W Quit-M1TAB
;----------------------------------
DoEdit  
 CLR.L  -(SP)    ;Check if DA in front
 _FrontWindow
 MOVE.L (SP)+,A0
 CMP.L  TheWind(A5),A0
 BEQ  DoScrap    ;branch if our wind in front
 CLR.B  -(SP)    ;System Wind Frontmost
 MOVE.W MENU+2(A5),-(SP)
 SUBQ.W #1,(SP)  ;balance different numbering
 _SysEdit
 TST.B  (SP)+    ;get rid of bool
 BRA  LOOP
;------------------------------
NewPoly
 CLR.W  PolyDrawn(A5);clear poly
 CLR.W  WhileDrawing(A5)
 CLR.W  NumPoints(A5)
 CLR.W  Liscio(A5) ;no poly, no smooth
 MOVE.L DoHand(A5),-(SP)  ;Menu Handle
 MOVE.W #1,-(SP)
 PEA  ‘Smooth’
 _SetItem ;reset
 MOVE.L DoHand(A5),-(SP)
 MOVE.W #1,-(SP)
 _DisableItem    ;no smooth menu and
 MOVE.L FileHand(A5),-(SP);no print active
 MOVE.W #3,-(SP)
 _DisableItem
 BSR  CleanWind
 BRA  Loop
;------------------------------
PageSetup
 prExec PrOpen   ;open the driver
 BSR  CheckErr   ;driver ok
 CLR.W  -(SP)    ;Style Dialog
 MOVE.L PrintRec(A5),-(SP)
 prExec PrStlDialog
 MOVE.W (SP)+,D0 ;forget it
 prExec PrClose
 BRA  Loop
; =========================================
Print
 MOVE.L D3,-(SP) ;save D3
 PEA  SavePort(A5) ;Save current port
 _GetPort
 prExec PrOpen   ;open the driver
 BSR  CheckErr   ;driver opened successfully ?

 CLR.W  -(SP)    ;Job Dialog
 MOVE.L PrintRec(A5),-(SP)
 prExec PrJobDialog
 MOVE.W (SP)+,D0
 BEQ  PrintDon   ;canceled?
 
 BSR  OroCurs    ;put watch curs 
 CLR.L  -(SP)    ;Open Print Port
 MOVE.L PrintRec(A5),-(SP)
 CLR.L  -(SP)
 CLR.L  -(SP)
 prExec PrOpenDoc
 MOVE.L (SP)+,PrintPort(A5)
 BSR  CheckErr   ;problems?
 
 MOVE.L PrintRec(A5),A0 ;Initialize New Graph Port
 MOVE.L (A0),A0
 PEA  prInfo+rpage(A0)    
 _ClipRect
 
 MOVEQ  #1,D3    ;assume Spool
 MOVE.L PrintRec(A5),A0
 MOVE.L (A0),A0
 TST.B  prJob+bjDocLoop(A0) ;Check if Draft or Spool
 BNE.S  @1
 MOVE.W prJob+iCopies(A0),D3;Set Num copies if Draft
@1 SUBQ.W #1,D3  ;for DBRA
 
PageLoop
 MOVE.L PrintPort(A5),-(SP) ;get ready to draw
 CLR.L  -(SP)    
 prExec PrOpenPage ;new page
 BSR  CheckErr   ;are we ok?
 
 MOVE.L CurPen(A5),-(SP)  ;Set size and 
 _PenSize
 MOVE.L CommentHand(A5),A1;scaling
 MOVE.L (A1),A0  ;deference handle
 MOVE.W #12,(A0)+;numerator = 72/6 and 
 MOVE.W CurScale(A5),(A0) ;denominator in dpi/6LongComment     SetLineWidth,4,A1
 
 BSR  MakePoly   ;do it

 MOVE.L PrintPort(A5),-(SP)
 prExec PrClosePage;page done
 BSR  CheckErr
 
 DBRA D3,PageLoop;next page
 
 MOVE.L PrintPort(A5),-(SP)
 prExec PrCloseDoc ;document done
 BSR  CheckErr
 
 MOVE.L PrintRec(A5),A0
 MOVE.L (A0),A0
 TST.B  prJob+bjDocLoop(A0) ;Check if Draft or Spool
 BEQ.S  PrintDon ;Branch if draft
 
 MOVE.L PrintRec(A5),-(SP);spool to printer
 CLR.L  -(SP)
 CLR.L  -(SP)
 CLR.L  -(SP)
 PEA  PrStatus(A5)
 prExec PrPicFile;print spool file
 BSR  CheckErr
 
PrintDonprExec PrClose  ;close driver
 MOVE.L SavePort(A5),-(SP)
 _SetPort ;restore port
 MOVE.L (SP)+,D3 ;register and
 _InitCursor;arrow curs
 BRA  Loop
; --------------------
CheckErrCLR.W  -(SP) ;WARNING (¿)
 prExec PrError  ;if an error occurs we have no handler
 MOVE.W (SP)+,D0 ;Err code in D0
 BNE  Error ;this just goes to the finder
 RTS
; ===============================
Quit  _ExitToShell ;ciao
; ===============================
DoScrap
 TST.W  PolyDrawn(A5)
 BEQ  Loop;nothing to clip
 
 PEA  WRect(A5)  ;(¿) Not a correct rect
 _ClipRect;it’s here just to save code

 CLR.L  -(SP)
 PEA  WRect(A5)  ;(¿) should use a better rect
 _OpenPicture    ;(¿) Do we have enough mem for a pict?
 MOVE.L (SP)+,A2 ;PicHandle
 ShortComment  PicDwgBeg  ;yes, it’s the format you know BSR   MakePoly
 ;do it
 ShortComment  PicDwgEnd
 _ClosePicture

 BSR  ClipWind   ;restore clip
 CLR.L  -(SP)
 _ZeroScrap
 MOVE.L (SP)+,D0
 BNE  Error
 MOVE.L A2,A0
 _GetHandleSize  ;how big is the pict
 CLR.L  -(SP)
 MOVE.L D0,-(SP) ;lenght
 MOVE.L #’PICT’,-(SP);ResType
 MOVE.L (A2),-(SP) ;Ptr Pict
 _PutScrap;put it down
 MOVE.L (SP)+,D0
 BNE  Error
 MOVE.L A2,-(SP)
 _KillPicture    ;Get rid of it
 BRA  Loop
; ------------------------------
DoDo    ;Smooth or unsmoooth
 MOVE.L DoHand(A5),-(SP)  ;Menu Handle
 MOVE.W #1,-(SP) ;item num
 NOT.W  Liscio(A5) ;set & check Smooth flag
 BEQ.S  @0;branch accordingly
 LEA  DrawSmooth(PC),A3 ;routine to call in A3
 PEA  ‘Unsmooth’ ;load proper name
 BRA.S  @1
@0 LEA  DrawPoly(PC),A3   ;no smooth
 PEA  ‘Smooth’   
@1 _SetItem 
 _OpenRgn
 JSR  (A3);WARNING :(¿) should make sure our poly is not
 MOVE.L RgnHand(A5),-(SP) ;too big to fit in a region
 _CloseRgn;if it doesn’t fit, we are dead
 BSR  CleanWind
 BSR  ReDraw
 BRA  Loop
; ===============================
DoPensize
 UnChkItm PenHand,CurPen(A5);uncheck old
 MOVE.W MENU+2(A5),CurPen(A5) ;v
 MOVE.W MENU+2(A5),CurPen+2(A5)  ;h
 CheckItm PenHand,CurPen(A5);check new
 TST.W  PolyDrawn(A5)
 BEQ  Loop
 BSR  SetPen
 BSR  CleanWind
 BSR  Redraw
 BRA  Loop
;------------------------------
DoPat
 UnChkItm FpatHand,ChkPat(A5) ;uncheck old
 MOVE.W MENU+2(A5),ChkPat(A5)
 CheckItm FpatHand,ChkPat(A5) ;check new
 MOVE.W MENU+2(A5),D0
 SUBQ.W #1,D0
 ADD.W  D0,D0
 MOVE.W PatTab(PC,D0.W),CurPat(A5) ;get offset constant to PAT
 TST.W  PolyDrawn(A5)
 BEQ  Loop
 BSR  CleanWind
 BSR  ReDraw
 BRA  Loop
PatTab  DC.W-8   ;White
 DC.W -32 ;LtGray
 DC.W -24 ;Gray
 DC.W -40 ;DkGray
 DC.W -16 ;Black
; --------------------------------
DoPenscale
 UnChkItm PscaleHand,Chkscale(A5)  ;uncheck old
 MOVE.W MENU+2(A5),Chkscale(A5)
 CheckItm PscaleHand,Chkscale(A5)  ;check new
 MOVE.W MENU+2(A5),D0
 SUBQ.W #1,D0
 ADD.W  D0,D0
 MOVE.W SclTab(PC,D0.W),CurScale(A5) ;get scale factor
 BRA  Loop
SclTab  ;unsign byte values (0-255)
 DC.W 12;  72/6
 DC.W 25; 150/6
 DC.W 50; 300/6
 DC.W 100 ; 600/6
 DC.W 200 ;1200/6
; =================================
SetPen
 TST.W  WhileDrawing(A5)  ;don’t change pen if drawing poly
 BNE.S  @0
 MOVE.L CurPen(A5),-(SP)
 _PenSize
@0 RTS
; -------------------------------- 
Redraw  ;no poly to work with
 TST.W  CurPat(A5) ;is our poly filled ?
 BEQ.S  @0;No fill if CurPat=Ø
 BSR  FillPoly
@0 TST.WLiscio(A5) ;is poly smoothed ?
 BNE.S  DrawSmooth
DrawPoly
 MOVE.W NumPoints(A5),D4  ;Total num of points
 SUBQ.W #2,D4    ;sub 1 for DBRA and 1 for _Moveto
 BGT.S  @0;check reasonable poly (>2 points)
 RTS    ;return if not to consider a poly
@0 MOVE.L PolyHand,A0;No surprise
 _HLock ;Line and LineTo can cause relocation
 BNE  Error
 MOVE.L (PolyHand),A3;Pointer to Poly data
 MOVE.L (A3)+,-(SP);first point
 _MoveTo
NextLine
 TST.W  (A3);Check for bit 15 (sign)
 BPL.S  LongPt   ;IF set do Long ELSE do Short

ShortPt MOVE.B (A3)+,D1   ;dY: a signed 7 bits offset
 BTST #6,D1 ;Test sign bit:a negative offset ?
 BNE.S  @0;yes, flag bit can stay
 BCLR #7,D1 ;else (if positive) clear it
@0 EXT.WD1;DY as a word
 SWAP D1;in hi word D1 now
 MOVE.B (A3)+,D1 ;signed byte dx
 EXT.W  D1;dx in low word
 MOVE.L D1,-(SP) ;offset on stack
 _Line  ;draw to
 BRA.S  NxLn

LongPt  MOVE.L (A3)+,-(SP)
 _LineTo;a point
NxLn  DBRAD4,NextLine

 MOVE.L PolyHand,A0;free again
 _HUnLock
 BNE  Error
 RTS
; --------------------------
DrawSmooth
 BSR  UnPackPoly ;we need normal points to work
 MOVE.L PolyBuffer(A5),A0 ;our temporary poly
 _HLock
 BNE  Error
 MOVE.L PolyBuffer(A5),A0
 ;Emulating a High-Level call
 MOVE.W NumPoints(A5),-(SP) ;First param 
 MOVE.L (A0),-(SP) ;second, a Ptr to points list
 BSR  SmoothPoly ;fortissimo
 
 MOVE.L PolyBuffer(A5),A0
 _HUnLock ;free again
 BNE  Error
 RTS
;==============================================================
; SmoothPoly (NumPt:INTEGER; PtrPolyList:PTR)
; (¿) Implementation good for poly side<1024 pixels of length
; The resulting smoothed polygon closely resembles the laser output
; ------------------------------------------------------------
LineTo  EQU $91  ;trap num
; ----------------------------------
; ---------- Stack Frame ----------
; ----------------------------------
NumPt   EQU 12
PtrList EQU 8    
ParamBytesEQU  6
;
AEQU  -2
BEQU  -4
Av EQU  -6
Bv EQU  -8
XEQU  -10
YEQU  -12
Line    EQU -16
OldW    EQU -18
OldQ    EQU -20

Locals  EQU -20
; ====================================================
; The following is a Basic translation of the algorithm 
; Comments in the Asm source refer to these integer variables
; Note that the two version slightly differs where necessary
; P(c,n) is a bidimensional array of points:
; P(0,n)=Y P(1,n)=X ; n=point index
; ----------------------------------------------------
; X1=P(1,1)-P(1,0):Y1=P(0,1)-P(0,0)
; X=X1\2+P(1,0):Y=Y1\2+P(0,0)
; MOVETO X,Y
; FOR j=1 TO NumPt
;    X0=X1:Y0=Y1
;    X1=P(1,j+1)-P(1,j):Y1=P(0,j+1)-P(0,j)
;    T=(ABS(X1+X0)+ABS(Y1+Y0))\8
;    IF T<2 THEN T=2
;    Z=T*T*2:Du=T+1
;   FOR n=0 TO T
;Du=Du-1:Q=n*n:w=Du*Du
;A=(Q*X1-w*X0)/Z+P(1,j)
;B=(Q*Y1-w*Y0)/Z+P(0,j)
;LINETO A,B
;   NEXT
; NEXT
; LINETO X,Y
; ==================================
SmoothPoly;If called from hi level think of it
 LINK A6,#Locals ;like a ToolBox procedure
 MOVEM.LA1-A4/D0-D7,-(SP) ;save the world
 
 MOVE.L PtrList(A6),A4    ;A4 point list Ptr
 MOVE.W #LineTo,D0 ;Trap num for LineTo
 _GetTrapAddress ;save some cycle
 MOVE.L A0,A3    ;store in A3 for later use
 CLR.L  Bv(A6)   ;clear last drawn point
 
 MOVE.L (A4),D4  ;First Pt
 MOVE.L 4(A4),D5 ;second Pt
 SUB.W  D4,D5    ;X1
 MOVE.W D5,D1    ;safe in D1
 SWAP D4;push y in low word
 SWAP D5
 SUB.W  D4,D5    ;Y1
 MOVE.W D5,D0    ;safe in D0
 ASR.W  #1,D1    ;divs by 2 with no sign loss
 ASR.W  #1,D0
 ADD.W  D4,D0    ;D0=Y+(Y2-Y1)/2
 ADD.W  2(A4),D1 ;D1=X+(X2-X1)/2
 MOVE.W D0,Y(A6) ;First point recorded for closing
 MOVE.W D1,X(A6)
 MOVE.W D1,-(SP)
 MOVE.W D0,-(SP)
 _MoveTo
 SUBQ.W #1,NumPt(A6) ;ready to start 
NextPoint
 MOVE.L D5,D4    ;Y0=Y1 : X0=X1
 ADDQ.L #4,A4    ;Current=Next
 MOVE.L 4(A4),D5 ;Next Point in D5
 SUB.W  2(A4),D5 ;X1=NextX-CurX
 MOVE.W D5,D6    ;safe in D6
 SWAP D5;push Y down
 SUB.W  (A4),D5  ;Y1=NextY-CurY
 MOVE.W D5,D0    ;safe in D0
;Calc num iteration now (T) ------------
 ADD.W  D4,D0    ;D0=Y0+Y1
 BPL.S  @1
 NEG.W  D0;ABS
@1 SWAP D4;x in low word
 ADD.W  D4,D6    ;D6=X0+X1
 BPL.S  @2
 NEG.W  D6;ABS
@2 SWAP D4;y back in low
 
 ADD.W  D0,D6    ;D6=(D0^2+D6^2)
 LSR.W  #3,D6    ;T=D6\8 (step)
 CMP.W  #2,D6    ;T=>2 at least
 BPL.S  @0
 MOVEQ  #2,D6    ;T=D6
@0 MOVE.W D6,D7
 MULU D7,D7 ;T*T
 LSL.W  #1,D7    ;Z=D7 (T*T*2)
 MOVEQ  #0,D3    ;n=0

 MOVE.W D6,D0    ;init values
 ADDQ.W #1,D0
 MULU D0,D0
 MOVE.W D0,OldW(A6)
 MOVE.W #1,OldQ(A6)
Curva
 MOVE.W D6,D0    ;Calc W (T*T), avoiding slow MULU
 ADD.W  D0,D0    
 ADDQ.W #1,D0
 SUB.W  D0,OldW(A6);W=(OldW)-2T+1
 MOVE.W OldW(A6),D0

 MOVE.W D3,D1    ;Calc Q (n*n), avoiding slow MULU
 ADD.W  D1,D1    
 SUBQ.W #1,D1
 ADD.W  D1,OldQ(A6);Q=(OldQ)+2n-1
 MOVE.W OldQ(A6),D1
 
 ADDQ.W #1,D3    ;n=n+1

 MOVE.W D0,D2    ;Calc B ------
 MULS D4,D2 ;Y0*W
 MOVE.L D2,A2
 MOVE.W D1,D2
 MULS D5,D2 ;Y1*Q
 SUB.L  A2,D2
 DIVS D7,D2 ;/Z
 ADD.W  (A4),D2  ;+ CurPt Y
 MOVE.W D2,B(A6) ;safe
 
 SWAP D4;push Xs down
 SWAP D5

 MOVE.W D0,D2    ;Calc A ------
 MULS D4,D2 ;X0*W
 MOVE.L D2,A2
 MOVE.W D1,D2
 MULS D5,D2 ;X1*Q
 SUB.L  A2,D2
 DIVS D7,D2 ;/Z
 ADD.W  2(A4),D2 ;+ CurPt X
 MOVE.W D2,A(A6) ;save
 SWAP D4
 SWAP D5
 
 CMP.W  Av(A6),D2;did we move? (over the unit)
 BNE.S  @0;much faster to check it here, than go 
 MOVE.W B(A6),D2 ;through the ROM with a useless Lineto
 CMP.W  Bv(A6),D2
 BEQ.S  @1
 
@0 MOVE.L B(A6),-(SP)
 JSR  (A3);LineTo
 MOVE.L B(A6),Bv(A6)
@1 DBRA D6,Curva

 SUBQ.W #1,NumPt(A6)
 BGT  NextPoint

Done  MOVE.LY(A6),-(SP)   ;back to first point
 JSR  (A3);LineTo
 
 MOVEM.L(SP)+,A1-A4/D0-D7 ;restore the world
 UNLK A6
 MOVE.L (SP)+,A0 ;RTS
 ADDQ #ParamBytes,SP
 JMP  (A0);back home
; --------------------------------
UnPackPoly
 MOVE.L PolyBuffer(A5),A0 ;copy the poly here
 MOVE.L (A0),A0  ;Ptr to temporary space
 MOVE.W NumPoints(A5),D0
 MOVE.W BytesWritten(A5),D1
 MOVE.L (PolyHand),A1;Ptr to packed poly in A1
 MOVE.L (A1)+,(A0)+;first point
 SUBQ.W #1,D0    ;one pt processed
 MOVE.L (A1),-4(A1,D1.W)  ;add 2nd pt to the end of list
Spacca  TST.W  (A1);Check if short or long point
 BMI.S  @0
 MOVE.L (A1)+,(A0)+;nothing to unpack
 DBRA D0,Spacca
 BRA.S  SpacDon
@0 MOVE.L -4(A0),(A0);point to add the offset to
 MOVE.B (A1)+,D1 ;dY: a signed 7 bits offset
 BTST #6,D1 ;a negative offset ?
 BNE.S  @1;yes, flag bit can stay
 BCLR #7,D1 ;positive: clear flag bit
@1 EXT.WD1;DY as a word
 ADD.W  D1,(A0)+
 MOVE.B (A1)+,D1 ;signed byte dx
 EXT.W  D1;dx in low word
 ADD.W  D1,(A0)+
 DBRA D0,Spacca
SpacDon RTS
; ========================================================
FillPoly
 MOVE.L RgnHand(A5),-(SP) ;Our Handle
 MOVE.W CurPat(A5),D0;Offset to pat
 MOVE.L (A5),A0  ;Ptr to QD’s GlobalsPEA     0(A0,D0.W)        ;fill 
it with Current Pat
 _FillRgn
 RTS
;------------------------------
CleanWind
 MOVE.L TheWind(A5),A0
 PEA  PortRect(A0)
 _EraseRect
 BSR  PutGrow    ;show it
 RTS
; ------------------------------
ClipWind
 MOVE.L TheWind(A5),A0
 PEA  PortRect(A0)
 _ClipRect
 RTS
; ------------------------------
PutGrow 
 MOVE.L #$10001,-(SP)
 _PenSize ;Use pen 1,1 to draw
 MOVE.L TempRgn(A5),-(SP) ;save
 _GetClip
 PEA  GrowBox(A5);only the box
 _ClipRect
 MOVE.L TheWind(A5),-(SP)
 _DrawGrowIcon
 MOVE.L TempRgn(A5),-(SP) ;put back
 _SetClip
 BSR  SetPen;restore pen
 RTS
; ------------------------------
CalcGrow
 MOVE.L TheWind(A5),A0
 LEA  GrowBox(A5),A1 ;Recalculate new GrowBox
 MOVE.L PortRect+4(A0),(A1) ;push bottom right point of window
 SUB.W  #15,(A1)+;make Top left box
 SUB.W  #15,(A1)+
 MOVE.L PortRect+4(A0),(A1) ;and bottom right
 RTS
; ------------------------------
ChkDoubClick;Check if a double click
 CLR.W  DoubleClick(A5)   ;assume not
 MOVE.L WHEN(A5),D1;this click
 SUB.L  LastClick_T(A5),D1;ticks elapsed since last
 CMP.L  DoubleTime,D1;close enough togather?
 BGT.S  @0;branch if over ‘DoubleTime’
 MOVE.W WHERE(A5),D0 ;now check for position
 SUB.W  LastClick_W(A5),D0;delta Y
 BPL.S  @1;ABS
 NEG.W  D0
@1 MOVE.W WHERE+2(A5),D1  ;delta X
 SUB.W  LastClick_W+2(A5),D1
 BPL.S  @2;ABS
 NEG.W  D1
@2 ADD.WD0,D1    ;Dx+Dy
 CMP.W  #3,D1    ;allow for a 3 pix max distance
 BGT.S  @0
 STDoubleClick(A5) ;yes, it’s a double click
@0 MOVE.L WHEN(A5),LastClick_T(A5);Record time and 
 MOVE.L WHERE(A5),LastClick_W(A5)  ;location
 RTS
; --------------------------------------
ActMenu BSR paramOnStack  ;load the stack
 _EnableItem;3 items
 _EnableItem;to enable
 _EnableItem
 _DrawMenuBar
 RTS
DimMenu BSR paramOnStack  ;load the stack
 _DisableItem    ;3 items
 _DisableItem    ;to disable
 _DisableItem
 _DrawMenuBar
 RTS
; ------
paramonStack
 MOVE.L (SP)+,A0 ;RTS in A0
 MOVE.L EditHand(A5),-(SP)
 MOVE.W #1,-(SP) ;undo
 MOVE.L EditHand(A5),-(SP)
 MOVE.W #3,-(SP) ;cut
 MOVE.L EditHand(A5),-(SP)
 MOVE.W #5,-(SP) ;paste
 JMP  (A0)
; ==========================================
MakePoly
 ShortComment  PolyBegin  ;Here starts our special Poly
 ShortComment  PicPlyClo  ;always a closed Poly in our case
 
 TST.W  Liscio(A5) ;is our poly to be smoothed?
 BEQ.S  @1;branch if not

 MOVE.L CommentHand(A5),A1;Handle to smooth verbs
 MOVE.L (A1),A0  ;deference
 MOVEQ  #Frame+Close,D0 ;assume only closed and framed
 TST.W  CurPat(A5) ;is our poly filled ?
 BEQ.S  @0
 ADDQ.W #Fill,D0 ;add fill verb
@0 MOVE.B D0,(A0);set verb = Close+Frame+(Fill)
 LongCommentPolySmooth,1,A1 ;smooth flag true

 MOVE.L TempRgn(A5),-(SP) ;save clip
 _GetClip
 PEA  ZeroRect(PC) ;if not aware of comments then no print
 _ClipRect
 BSR  DrawPoly;Pass laser original vertexes to be smoothed
 MOVE.L TempRgn(A5),-(SP)
 _SetClip ;allow others to print
 
 ShortComment  PolyIgnore ;ignore following poly if laser

@1 BSR  ReDraw   ;simulate spline for non-laser printers
 ShortComment  PolyEnd    ;End special poly
 RTS
;===========================================
InitGlobals
 CLR.W  CurPat(A5) ;No fill pat
 MOVE.L #$10001,CurPen(A5);PenSize 1,1
 MOVE.W #12,CurScale(A5)  ;72 dpi
 MOVE.W #1,ChkPat(A5);first pat checked
 MOVE.W #1,Chkscale(A5) ;72 dpi checked
 RTS
; ----------------------------------
InitMac
 PEA  -4(A5);Recite Mac pray
 _InitGraf
 _InitFonts
 _InitWindows
 _InitMenus
 CLR.L  -(SP)    ; No restart procedure
 _InitDialogs
 _TEInit
 _InitCursor
 MOVE.L #$0000FFFF,D0; Flush all events
 _FlushEvents
 RTS
; ------------------------------------
SetWindow
 MOVE.L (A5),A0  ;Pointer to QuickDraw Globals
 LEA  screenBits(A0),A0 ;Ptr to Bounds of Screen BitMap
 MOVE.L 6(A0),WRect(A5)   ;Get & set Screen Rect
 MOVE.L 10(A0),WRect+4(A5)
 PEA  WRect(A5)
 MOVE.L #$100010,-(SP)    ;allow 16 Pixels inside
 _InsetRect
 MOVE.L WRect(A5),WBounds(A5) ;Init Draging bounds Rect
 MOVE.L WRect+4(A5),WBounds+4(A5)

 MOVE.L #$400080,WSize(A5);Grow Rect (64,128,768,768)    MOVE.L
 #$3000300,WSize+4(A5)  ;(¿) should calc this rect
 ;according to PageSetUp
 MOVE.W MBarHeight,D0;Center WRect on video
 ADDI.W #18,D0   ;Title bar
 ADD.W  D0,WRect(A5) ;subtract Menu Height

 CLR.L  -(SP)    ;Create our window
 CLR.L  -(SP)
 PEA  WRect(A5)
 PEA  ‘Polygon’  ;document:Polygon
 MOVE.B #-1,-(SP)
 MOVE.W #8,-(SP) ;has zoom box
 MOVE.L #-1,-(SP)
 CLR.B  -(SP)
 CLR.L  -(SP)
 _NewWindow
 MOVE.L (SP),TheWind(A5)  ;leave it on stack and save
 _SetPort
 BSR  CalcGrow
 BSR  ClipWind
 BSR  PutGrow
 RTS
;=================================
SetMenus
 MOVEM.LA3/D3-D5,-(SP)    ;save stuff
 LEA  MenuHndList(A5),A3  ;Stuff with MHandles from here on
 MOVEQ  #1,D3    ;Start with Menu 1
 MOVEQ  #4-1,D4  ;4 menus (-1 for DBRA)
 MOVEQ  #0,D5    ;before ID = 0    
 BSR  PushMenus  ;sequential ordering

 MOVE.L MelaHand(A5),-(SP)
 MOVE.L #’DRVR’,-(SP)
 _AddResMenu;yes, we support DA
 ;add submenus
 MOVEQ  #’A’,D3  ;Start with Menu 65
 MOVEQ  #2,D4    ;through menu 67
 MOVEQ  #-1,D5   ;hierarchical beforeID
 BSR  PushMenus

 CheckItm PenHand,#1 ;check items
 CheckItm FpatHand,#1
 CheckItm PscaleHand,#1

 _DrawMenuBar
 MOVEM.L(SP)+,A3/D3-D5  ;restore stuff
 RTS
; ------------------
PushMenus
 CLR.L  -(SP)
 MOVE.W D3,-(SP) ;ID of Menu to get
 _GetRMenu
 MOVE.L (SP),(A3)+ ;Save Hndl trusting global ordering
 MOVE.W D5,-(SP) ;Hndl was left on stack + beforeID
 _InsertMenu
 ADDQ.W #1,D3    ;Next ID
 DBRA D4,PushMenus
 RTS
; --------------------------------------
GetHandles
 MOVE.L #$7FFF,D0;ask for 32K
 _NewHandle
 BNE  Error
 MOVE.L A0,PolyHand
 
 MOVE.L #$FFFE,D0;ask for 64K
 _NewHandle
 BNE  Error
 MOVE.L A0,PolyBuffer(A5)
 
 MOVEQ  #120,D0  ;Print Record Size
 _NewHandle
 MOVE.L A0,PrintRec(A5)
 MOVE.L A0,-(SP)
 prExec PrintDefault ;Fill it with defaults
 
 MOVEQ  #8,D0    ;enough for any comment data
 _NewHandle
 BNE  Error
 MOVE.L A0,CommentHand(A5)
 
 CLR.L  -(SP)    ;our fill region
 _NewRgn
 MOVE.L (SP)+,RgnHand(A5)
 
 CLR.L  -(SP)
 _NewRgn
 MOVE.L (SP)+,TempRgn(A5) ;A dummy region
 RTS
;========================================
ERROR BSR Bippa  ;WARNING (¿)
 BSR  Bippa ;Best Error Handler routine ever written
 BSR  Bippa ;but I know you could do even better
 _ExitToShell
; ----------------------------
Bippa MOVE.W#3,-(SP)
 _SysBeep
 RTS
; ----------------------------
OroCurs CLR.L  -(SP) ;watch cursor
 MOVE.W #4,-(SP)
 _GetCursor
 MOVE.L (SP)+,A0
 MOVE.L (A0),-(SP)
 _SetCursor
 RTS
; ----------------------------------------------------
ZeroRectDC.W0,0,0,0
; ----------------------------------------------------
; ------------------ GLOBALS ------------------------
; ----------------------------------------------------
WRect   DS.B8  ;Window stuff
GrowBox DS.B8
WBounds DS.B8
WSize   DS.B8
TheWind DS.L1
WINDOW  DS.L1

MenuHndList DS.L 0 ;do not change this ordering!
MelaHandDS.L1  ;Menu Handles
FileHandDS.L1
EditHandDS.L1
DoHand  DS.L1
PenHand DS.L1  ;Hierarchical Menu Handles
FpatHandDS.L1
PscaleHandDS.L 1

EVENTRECORD DS.B 0 ;My event.
WHAT    DS.W1
MESSAGE DS.L1
WHEN    DS.L1
WHERE   DS.L1
MODIFY  DS.W1

MENU    DS.L1  ;Other 
PolyBufferDS.L 1
PolyDrawn DS.W 1 
whileDrawingDS.W 1
NumPoints DS.W 1
BytesWrittenDS.W 1
LastPoint DS.L 1
CurPointDS.L1
OldPointDS.L1
MousLoc DS.L1
LastClick_T DS.L 1
LastClick_W DS.L 1
DoubleClick DS.W 1
CurPat  DS.W1
CurPen  DS.L1
CurScaleDS.W1
ChkPat  DS.W1
ChkscaleDS.W1
Liscio  DS.W1
RgnHand DS.L1
TempRgn DS.L1
CommentHand DS.L 1
DskName DS.B64
SavePortDS.L1
PrintPort DS.L 1
PrintRecDS.L1
PrStatusDS.B26
ItmHit  DS.W1

{3}
Listing:  Smooth.R
* Resources for Smoother
* (Make sure blank lines don’t have space characters in them!)

SmoothRes.Rel
APPLDEMO

TYPE  DEMO = GNRL
,0
.P
v 1.0 by Raul Tabasso - Roma 22/6/88

TYPE  FREF
 ,128
APPL 0

TYPE  BNDL
 ,128
DEMO 0
ICN# 
0 128
FREF 
0 128

TYPE  ICN# = GNRL;; Appl Icon
,128
.H
0000 0000 0000 0000 0000 0000 0000 3FC0
0001 C030 0002 0008 0004 0004 0004 0002
0008 0002 0008 0002 0008 0182 0008 0204
0004 0208 0002 01F0 0001 0000 0000 8000
0000 6000 0000 1800 0000 0700 0000 00C0
0000 0030 0000 000C 0000 0004 0000 0002
0000 0002 0000 0002 0000 0002 0000 0004
0000 0004 0000 0008 0000 0030 1FFF FFC0
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF

FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
* ----------------------------------------
* -- MENUS

TYPE  MENU
 ,1;;Apple menu  
\14;;Apple char
About Smoother 
(-

TYPE  MENU
 ,2
File    ;;File menu
New polygon/N
Page Setup 
(Print 
Laser pen scaling/\1B!C ;;submenu 67
Quit/Q

TYPE  MENU
 ,3
Edit    ;;Edit menu
Undo/Z
(-
Cut/X
Copy/C
Paste/V

TYPE  MENU
 ,4
Options ;;Polygon menu
(Smooth/S
Pen size/\1B!A ;;submenu 65
Fill tone/\1B!B  ;;and 66

TYPE  MENU
 ,65
Psize   ;; Pen Size submenu
1 Pt.^;
2 Pt.^<
3 Pt.^=
4 Pt.^>
5 Pt.^?

TYPE  MENU
 ,66
Fpat    ;; Fill Pattern submenu
No fill^1
25%^2
50%^3
75%^4
Black^5

TYPE  MENU
 ,67
PScale  ;; Pen Scaling submenu
72 dpi
150 dpi
300 dpi
600 dpi
1200 dpi

TYPE  DLOG
 ,256 ;;ID
The about dialog
80 80 220 400  ;;Rect
Visible NoGoAway
1;;proc ID
0;;ref con
256;;DITL related

TYPE  DITL
 ,256
2

Button
100 200 120 256
OK

StaticText
32 64 80 256
Smoother by Raul Tabasso. ++
An example program on polygon’s comments

* ----------------------------------------
* -- Icons for submenu n° 66 ------------

TYPE  ICON = GNRL
 ,257
.H
FFFF FFFF FFFF FFFF C000 0003 C000 0003 
C000 0003 C000 0003 C000 0003 C000 0003 
C000 0003 C000 0003 C000 0003 C082 3C03 
C0C2 6603 C0E2 6603 C0F2 6603 C0BA 6603 
C09E 6603 C08E 6603 C086 6603 C082 3C03 
C000 0003 C000 0003 C000 0003 C000 0003 
C000 0003 C000 0003 C000 0003 C000 0003 
C000 0003 C000 0003 FFFF FFFF FFFF FFFF 

TYPE  ICON = GNRL
 ,258
.H
FFFF FFFF FFFF FFFF E222 2223 C000 0003 
C888 888B C000 0003 E222 2223 C000 0003 
C888 888B C000 0003 E222 2223 C000 0003 
C888 888B C000 0003 E222 2223 C000 0003 
C888 888B C000 0003 E222 2223 C000 0003 
C888 888B C000 0003 E222 2223 C000 0003 
C888 888B C000 0003 E222 2223 C000 0003 
C888 888B C000 0003 FFFF FFFF FFFF FFFF 

TYPE  ICON = GNRL
 ,259
.H
FFFF FFFF FFFF FFFF EAAA AAAB D555 5557 
EAAA AAAB D555 5557 EAAA AAAB D555 5557 
EAAA AAAB D555 5557 EAAA AAAB D555 5557 
EAAA AAAB D555 5557 EAAA AAAB D555 5557 
EAAA AAAB D555 5557 EAAA AAAB D555 5557 
EAAA AAAB D555 5557 EAAA AAAB D555 5557 
EAAA AAAB D555 5557 EAAA AAAB D555 5557 
EAAA AAAB D555 5557 FFFF FFFF FFFF FFFF 

TYPE  ICON = GNRL
 ,260
.H
FFFF FFFF FFFF FFFF DDDD DDDF F777 7777 
DDDD DDDF F777 7777 DDDD DDDF F777 7777 
DDDD DDDF F777 7777 DDDD DDDF F777 7777 
DDDD DDDF F777 7777 DDDD DDDF F777 7777 
DDDD DDDF F777 7777 DDDD DDDF F777 7777 
DDDD DDDF F777 7777 DDDD DDDF F777 7777 
DDDD DDDF F777 7777 DDDD DDDF F777 7777 
DDDD DDDF F777 7777 FFFF FFFF FFFF FFFF 

TYPE  ICON = GNRL
 ,261
.H
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 

* ----------------------------------------
* -- Icons for submenu n° 65 ------------

TYPE  ICON = GNRL
 ,267
.H
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 FFFF FFFC 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 

TYPE  ICON = GNRL
 ,268
.H
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 FFFF FFFC FFFF FFFC 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 

TYPE  ICON = GNRL
 ,269
.H
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 FFFF FFFC FFFF FFFC FFFF FFFC 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 

TYPE  ICON = GNRL
 ,270
.H
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
FFFF FFFC FFFF FFFC FFFF FFFC FFFF FFFC 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 

TYPE  ICON = GNRL
 ,271
.H
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 FFFF FFFC 
FFFF FFFC FFFF FFFC FFFF FFFC FFFF FFFC 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more
AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&T’s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more

Jobs Board

Sublease Associate Optometrist- *Apple* Val...
Sublease Associate Optometrist- Apple Valley, CA- Target Optical Date: Apr 20, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92307 **Requisition Read more
*Apple* Systems Administrator - JAMF - Syste...
Title: Apple Systems Administrator - JAMF ALTA is supporting a direct hire opportunity. This position is 100% Onsite for initial 3-6 months and then remote 1-2 Read more
Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.