|
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
- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine