PicComments
Volume Number: | | 4
|
Issue Number: | | 12
|
Column Tag: | | Assembly Lab
|
Inside PicComments
By Raul Tabasso, Rome, Italy
Inside PicComments
Everything youve 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 dont 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, Apples 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 printers 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 cant 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 isnt 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: Dont 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.
Lets 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 cant 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 dont 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);{dont 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.
Lets 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 doesnt 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 wont 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 dont 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.
Lets 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. Shouldnt 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 dont 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 its 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.
Lets now see a slightly different way to accomplish the same task which is derived from the new scheme used in MacDraw II. Basically it doesnt 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 well 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 wont 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 dont 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 doesnt 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
Lets 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.
Dont 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 doesnt appear to work as expected until you know its idiosyncrasies. Lets see the inner workings of this comment in conjunction with the laser driver.
For simplicity well 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 its different from the previously used value, it generates a pen LaserPreps 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 laserPreps 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? Dont ask me, but once you figure out the mechanism it shouldnt 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.
Heres 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 writers 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 couldnt 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 pictures 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;PrGlues 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 dont 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) ;cest 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 wont 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;doesnt 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 doesnt 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;its 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, its 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 doesnt 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) ;dont 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 QDs 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, its 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 dont 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 polygons 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