TweetFollow Us on Twitter

Regions 2
Volume Number:4
Issue Number:9
Column Tag:Pascal Procedures

Fun With Regions, Part II

By Stephen Dubin, V.M.D., Ph.D.,, Thomas W. Moore, PH.D., Drexel University, Sheel Kishore, MS

In our previous paper (Fun with Regions Part I: High Level Language Implementation), we showed how it was possible to estimate the area of an arbitrarily drawn region from high level languages such as Pascal and C using repeated application of the ROM subroutine, PtInRgn. Although this approach is simple and intuitive, execution time is excessive for large or complex regions. Whenever part of a high level language routine consumes an inconveniently long execution time, the possibility of using assembly language to achieve better efficiency should be considered. Two fundamental approaches may be applied. A study of the code generated by the compiler may reveal unnecessary looping, inefficient use of registers or other complexities which can be streamlined. If savings can be made within loops executed many times, the resulting speedup can be significant. The second approach applies when the task at hand is relatively simple and straightforward. In this case, determination of a specific efficient algorithm is the key, with translation to assembly code following directly. In dealing with the area computation part of our regions manipulation, we have explored both of these approaches and found the latter approach to be clearly superior.

In the first article, we provided the C Language code for the area calculation program at the end of the article with major subroutines interpolated into the text as implemented in Pascal. In this installment, the “tables are turned”; the interpolated routines are implemented for the Megamax C development system and the Consulair (MDS) assembly language system [This is included in the source code due to space considerations-ED]. At the end of the article, a program using the most useful of the assembly language optimizations is shown for Turbo Pascal with explanation of minor changes needed for the TML Pascal Development system. We recognize that our program may not be the most elegant or efficient approach to the problems; but even where an attempt at optimization yielded poor or marginal results, an interesting and - hopefully - useful technique is explored.

Figure 1.

Several authors ( Morton, M.: Reduce Your Time in the Traps! MacTutor October 1986 pp 21-24. and Knaster, S.: “How to Write Macintosh Software.” Hayden, Hasbrouck Heights NJ, 1986, p 368 ) have advocated bypassing the trap dispatcher as a means of speeding routines in which ROM calls are made repeatedly. Certainly our CountPix routine, since it calls PtInRgn for every point within the region bounding box, is a candidate for this type of optimization. Mike Morton presents the underlying mechanism for this strategy, points out some cautions and pitfalls and shows how to do this task in Pascal using INLINE calls. Briefly, any call to the ROM must go through an intermediate step of finding the “true” address of the call in the particular version of the ROM in your machine before it can be invoked. When a ROM call is to be used many times, this address may be determined one time by means of the GetTrapAddress function early in the program; then you may employ some means of jumping to this address directly whenever the particular ROM call is to be used. In C language, the address might be acquired as follows:

trap = gettrapaddress(0xa8e8);   /*  trap is a global long integer */
 /* A8E8 is the trap # for ptinrgn */

In order to see how the jump might be made, consider the following “glue” routine which is used for ptinrgn by the Megamax system:

boolean ptinrgn(pt, rgn)  /* as copied from qd13.c  */
point *pt;
rgnhandle rgn;
{
    asm {
 subq #2,A7 /* make room on the stack for the result */
 move.l pt(A6),A0/*  address of point into A0 */
 move.l (A0),-(A7) /*  dereference and put onto stack*/
 move.l rgn(A6),-(A7)/* region handle onto stack */
 dc.w 0xa8e8/* call the ROM for ptinrgn */
 move.b (A7)+,D0 /* result into D0 where C expects to find the answer 
*/
 ext.w D0 /* sign extend the result */
    }
}

Note that with Megamax inline assembly, the compiler takes care of setting up (and tearing down) the stack frame. Automatic (local) variables are accessed using the name of the variable as a displacement from A6. Global variables are treated similarly as offsets from A4. Thus if we have safely installed the true ROM address of ptinrgn in “trap”, we can write a “new improved” version of the glue routine as follows:

boolean zptinrgn(pt, rgn) /* same as ptinrgn except */
point *pt;/*  bypasses the trap dispatcher */
rgnhandle rgn;
{
    asm {
 subq #2,A7 /* make room on the stack for the result */
 move.l pt(A6),A0/* address of point into A0*/
 move.l (A0),-(A7) /* dereference and put onto stack */
 move.l rgn(A6),-(A7)/* region handle onto stack */
 move.l trap(A4),A2/* address of “true address” of ptinrgn into A2 */ 

 jsr  (A2)  /* dereference once and jump there */
 move.b (A7)+,D0 /* result into D0 where C expects to find the answer 
*/
 ext.w D0 /* sign extend the result */
    }
}

When this version was used in place of ptinrgn, the time needed to estimate the area of a region was decreased by 15% for small simple regions and about 9% for larger and more complex ones. Although this would ordinarily be considered a significant improvement, it is little comfort to know that a five minute computation can now be completed in only four and a quarter minutes.

Upon examination of the disassembled code for counbtpix(), we noticed that the most often used variables were the points delimiting the region bounding box as well as the “exploring” point on which we called ptinrgn. Following classical optimization strategy, the next step was to set up these data structures on registers rather than to fetch them every time the coordinates of the exploring point were incremented. The code for this implementation of the countpix function is shown below:

{1}
bcountpix(theregion) /* sets up test point on registers  */
rgnhandle theregion;
{

 
 asm{
 
 move.l trap(A4),A2; address of ptinregn
 move.w #0xA8E8,D0 ; trap number for ptinrgn
 dc.w   0xA146   ; call the trap, address is in A0
 move.l A0,A2    ; put it into A2
 
 move.l theregion(A6),A3  ; regionhandle 
 move.l (A3),A1  ; dereference once
 move.l 2(A1),D4 ; topleft of rgnbox
 move.l 6(A1),D5 ; botright of rgnbox
 move.l D4,D6    ; copy of TL as VH current point
 
hortest:
 cmp.w  D5,D6    ; compare horizontal
 blt.s  vertest  ; go on
 swap D4  ; D4 is now HV
 addq.w #1,D4    ; down 1 row
 swap D4; now D4 is back to HV
 move.l D4,D6    ; make this the current test point
 
vertest:
 swap D6; now is HV
 swap D5; now is RB
 cmp.w  D5,D6    ; compare vertical
 blt.s  pointest ; go on
 bra.s  done; 
 
pointest:
 swap D5; back to BR
 swap D6; back to VH
 subq #2,A7 ; make room for result
 move.l D6,-(A7) ; point onto stack
 move.l theregion(A6),-(A7) ; rgnhandle onto stack
 jsr  (A2); go to ROM
 move.b (A7)+,D0 ; result onto stack
 tst.b  D0; was it true?
 beq  skip; not this time  
 addq.l #1,numpix(A4); yes, increment the counter
skip:
 addq.w #1,D6    ; over 1 column
 bra.s  hortest  ; back for another point
 
done: 
 } 
}

As with the previous attempt at optimization, the speed increase with this approach was marginal at best. Our final attempt in this direction was to examine the code for ptinregn in the ROM in order to transpose (plagiarize?) it directly into the above routine. The result was surprising as well as disappointing. Although there was a measurable but tiny improvement for small simple regions, ones for which optimization was not needed anyway, the time needed to calculate the area of large, complex or disjoint regions increased significantly!! Our theory as to why this happens is based on the way in which the 68000 accesses ROM and RAM. Accesses to RAM (where the program resides) are shared with the video display, sound generator and disk speed controller. This leads to a RAM access rate of approximately six megahertz. The ROM has a “direct line” and is accessed at 7.83 MHz (Inside Macintosh. III-18, Addison-Wesley, Reading MA, 1985).

All of this preoccupation with ptinregn led to an understanding of why the area computation takes so long for large or complex regions. A flow chart of how ptinregn works is shown in figure 1. Unless the region under examination is rectangular, it may be necessary to examine all of the region data, one word at a time, before deciding whether the point is indeed in the region. This is particularly true as the exploring point moves toward increasing values in the vertical (y) component. Clearly, our original countpix procedure, which calls ptinregn on every point in the bounding box, covers the same ground many times. Based on a conviction that the region information should be adequate to permit estimation of the area with one pass, we resolved to implement a specific algorithm “from the ground up.”

As mentioned in the first installment of this article, region information is stored in memory in a way designed to require minimal space. A clear understanding of this method of encoding region boundaries is necessary in order to design our area calculating algorithm. To illustrate this process, consider the simple region plotted in Figure 2. The numbers on the plot are the coordinates of the “corners” of the outlined region. A memory dump of the data representing this region is shown below the graph. In order to design an algorithm for area calculation, we must understand the method of encoding the region in memory. As explained in our previous paper, the first five words of this data list are the data size in bytes (44) followed by the “upper left” and “lower right” coordinates of the rectangular boundary of the region - the regnbbox (100,100,220,200). Following these five words we find the information needed to compute area. Only horizontal boundary information is stored. The region being defined consists of the (rectangular) area under a given boundary line, extending down to the next horizontal boundary line encountered. Horizontal boundary lines are indicated by a y coordinate word, followed by start and stop x values (more than one pair if the line has multiple segments). The flag word, #7FFF, marks the end of the boundary segments at a particular y value. Therefore, the sixth word of data (100) is the y coordinate of the top boundary line of the region. It is followed by x values 100 (start of line) and 200 (end of line). Then we get a flag indicating that no more boundary segments exist at this level. The next horizontal boundary is the line from (125,150) to (180,150). Therefore we find 150 (y value) and 125,180 (x start and stop values) to be the next three words of the data. In this manner, the remaining data can be seen to define the region of figure 2. The double flag indicates the end of the data table.

The question now is how to use this table to calculate the area of the region thus defined. The arrangement of the data suggests dividing the area into rectangular pieces and adding their areas. We might start by subtracting the first x value (100) from the second (200) to get the width of the top of the first rectangle (marked “A”). The y value of this line could be put aside to be subtracted from the next y value (170) yielding the height of the rectangle. The product of these dimensions is one component of the final area

The x values following the 150 are endpoints of a new horizontal boundary line. Since this line falls under the previous boundary, it represents the bottom of a rectangular piece rather than the top of a new one. From here (y = 150 ) our region will now grow downward in two rectangular pieces, B and C. To calculate the areas of these pieces, we must make use of the two new x values found in the data table (125,180). If we arrange all x values found so far in order of magnitude (100, 125, 180, 200), the appropriate widths can be found by pairing the values and subtracting the first from the second in each pair. This is a rule we can use in our algorithm: maintain an ordered list of all encountered x values, pair them and subtract the first of each pair from the second. The sum of these differences will be the total width at the top of each of the rectangular regions. The y coordinate at the top of B and C is subtracted from the next y coordinate found (170-150) to determine the height of these rectangles. Height times width is then added to the accumulating total area.

One problem remains: how to end the process? Following y value 170, we find x values of 100 and 125 in the data. One leg of our descending area ends here so we would like our x table to list just 180, 200 (the top of D) from here on. Therefore, the final rule we need for our area algorithm is to remove entries from the x value ordered list whenever they are matched by a newly encountered x value. At the final y value (220), we subtract the remaining x pair (200-180), and multiply by the last y difference (220-170), giving the area of the last piece, D.

A flowchart of this process is given in figure 3. The routine as implemented to provide a linkable object file using the MDS (now Consulair) 68000 assembly system is shown below. Using the TTAA (Tom Terrific Area Algorithm), even the largest and most complex region that could be drawn on the Macintosh screen could have its area estimated in less than 20 seconds. Such regions take as long as ten minutes using the old CountPix.

This is the code for use with the MDS assembler to produce the file ACountPix.REL. This can then be linked to a Pascal or other “main” program.

{2}
;
;ACountPix.asm
;Pascal Usage: Function ACountPix( theRegion:RgnHandle) : LongInt;
;This function emulates CountPix
;  Written by Thomas W. Moore, Ph.D. and Stephen Dubin, V.M.D., Ph.D.
;Copyright © 1987
 
 XDEF  ACountPix
 XREF  myBUF
; The buffer is allocated in the calling program even though it might 
be
; more elegant to allocate it here with a DS statement; however Turbo
; Pascal V1.0 seems intolerant of this. ( p 335 of the Turbo Pascal manual)
;-------------------------------- INCLUDES ------------------------

Include Traps.D  ; Use System and ToolBox traps
Include ToolEqu.D; Use ToolBox equates



ACountPix:
 link A6,#0 ; set up frame pointer
 movem.lA0-A3/D0-D7,-(A7) ; save the world
 clr.l  -(A7)  ; make room on stack for result
 movea.l8(A6),A0 ; region handle into A0
 movea.l(A0),A0  ; dereference => pointer in A0
 clr.l  D7; set area to zero
 lea  myBUF(A5),A1 ; lowest address of x list
 
rectcheck:; see whether it is a rect and if so - do  the job here
 cmpi.w #10,(A0) ; is this a single rectangle
 bne.s  morework ; if not do the big job
 move.w 4(A0),D1 ; left
 move.w 8(A0),D2 ; right
 move.w 2(A0),D3 ; top
 move.w 6(A0),D4 ; bottom
 sub.w  D1,D2  ; width
 sub.w  D3,D4  ; height
 mulu.w D2,D4  ; area in D4
 move.w D4,D7  ; lower word into D7
 bra  done

morework: ; get ready for some serious work
 lea  10(A0),A0  ; beginning of region info
 clr.l  D4;
 clr.l  D2;
 move.l #512,D3  ; size of buffer to hold ordered list of x values
 adda.l D3,A1  ; highest address in buffer
 movea.lA1,A2  ; copy in A2
 movea.lA1,A3  ; another in A3
 move.w #-1,(A1) ; -1 in highest x address so that 1st x entry will be 
greater

gety:   ; read in y coordinate of next horizontalboundary       
 
 move.w (A0)+, D3; latest y value
 jsr  calc
 
getx:
 move.w (A0)+,D1 ; new x value
 cmpi.w #$7fff,D1; flag indicates no more x values at this y
 bne  storex; if no flag, it is a new x
 move.w (A0),D1  ; next word of region info
 cmpi.w #$7fff,D1; all done?
 beq  done; yes go home
 bra  gety; no, get next y

storex: ; place new x value in proper place in ordered list
 movea.lA3,A1  ; A3 points to highest x value in ordered list
 cmp.w  (A1),D1  ; compare new x value to largest entry
 bne  s1; if not equal, it must be added to list
 addq #2,A3 ; if match, remove from list
 bra  getx; next x
 
s1:
 lea  -2(A3),A3  ; add a space at high end of list for new x
 bgt.s  insert ; if new x value is greatest, put it on top
 
mkroom: ; new x is not greatest so we must   move list values up to make 
room
 move.w (A1)+,-4(A1) ; move data  up (1 word net distance)
 cmp.w  (A1),D1  ; compare next list entry
 beq.s  remove ; if it matches, remove it
 bcc.s  insert ; it is greater, so put it above
 cmpa.l A1,A2  ; are we at bottom?
 bne  mkroom; no, move another one up
 
insert: ; insert new x value in ordered place in list
 move.w D1,-(A1) ; insert above present location
 bra  getx;
 
remove: ; erases an entry from the list
 subq #2,A1 ; point to next higher
 
r1:
 cmpa.l A1,A3  ; is it the top?
 beq  shrink; yes so exit
 move.w -(A1),4(A1); move greater x values down to replace
 bra  r1; value removed
 
shrink:
 addq #4,A3 ; if a match occurred, list shrinks by 2 words
 bra  getx; one that we didn’t insert and one that we erased
 
calc:   ; determine new Height
 sub.w   D3,D4 ; Y old - Y new
 neg.w  D4; Height of the rectangle(s)
 
newW:   ; prepare for Width calculation
 clr.l  D2; Will receive width
 clr.l  D1; work reg
 movea.lA2,A1  ; reset A1 to point to least x value in list
 
dx:; check to see if all x pairs have been used. 
 ; multiply H x W and add to area
 cmpa.l A1,A3  ; A3 points to greatest x value in  list
 bne  morex ; if not equal, not all x’s have been  used
 mulu   D4,D2  ; H x W
 add.l  D2,D7  ; add to accumulating area
 move.w D3,D4  ; for next time
 rts
 
morex:  ; subtracts x values in pairs adding differences to accumulating 
W
 move.w -(A1),D1 ; Xi (lower x value of a pair)
 sub.w  -(A1),D1 ; Xi - Xi+1 (length of a horizontal boundary segment)
 neg.w  D1; Xi+1 - Xi (correct sign)
 add.w  D1,D2  ; W (add to accumulating width)
 bra  dx;
 
done:
 move.l D7,12(A6); store result “under” the last parameter
 movem.l(A7)+,A0-A3/D0-D7 ; restore registers
 unlk A6; restore original stack
 move.l (A7)+,A0 ; get return address
 addq.l #4,A7  ; remove parameters
 jmp  (A0); return this way
 
 end  

The same algorithm can be used with the very convenient inline assembly facility of the Megamax and other C development systems. Because these compilers take care of “tending the stack” for you, the entry and exit procedures are significantly simplified. For the Megamax systems, they are as follows:

{3}
acountpix(theregion)
rgnhandle theregion;
{
intbuf[1000];  
 asm{
 
 move.l theregion(A6),A0  ; regionhandle  note: local variables are 
 referred off A6
 move.l (A0),A0  ; dereference once => region pointer
 clr.l  D7; set area to zero
 lea  buf(A6),A1 ; lowest address of x list

 rectcheck:

/*  Everything in between is the same as in ACountPix.Asm above */ 
 
done:
 move.l D7,numpix(A4);report the answer note: global variables 
 are referred offA4 
 
 } 
}

This is the code for our main calling program as implemented in Turbo Pascal:

{4}
{PasArea.Pas }
{Copyright 1987 by Stephen Dubin, V.M.D.and Thomas W. Moore,Ph.D. }
{Prepared with Turbo Pascal V1.0                                  }
{ Users of other Pascal systems should particularly check the “preamble”}
{ portion of their program (Linking directives, “uses”, “includes”, etc.}
{ also check usage of type “point” - TML doesn’t like use of pt.h and}
{ pt.v as control elements in a for statement.                       
}

program PasArea; 

{$R-}               { Turn off range checking               }
{$I-}               { Turn off I/O error checking           }
{$R PasArea.rsrc}   { Identify resource file                }
{$U-}               { Turn off auto link to runtime units   }
{$L ACountPix.Rel } { Link in Assembly Language Segment}
{$D+}               { Embed Procedure Labels                }

uses  Memtypes,QuickDraw,OSIntf,ToolIntf,PackIntf;

const
  FileMenuID = 1;{ the File menu}
  OptionMenuID = 2;{ the option menu}
  WindResID = 1; { the resource id of my window}
 
type
  BUF   = array[1..512] of Integer; { Make it bigger if you are really 
paranoid}
  
var
  myMenus : Array[FileMenuId..OptionMenuID] of MenuHandle; 
  Done : Boolean;
  MyWindow : WindowPtr;   
  TotalRegion   :   RgnHandle;
  Numpix        :   Longint;
  myBUF         : BUF;
    
function ACountPix( theRegion:RgnHandle) : LongInt; external;

function CountPix(theRegion : RgnHandle): LongInt;       
var
 pt : Point;
 rgn    :   Region;
 temp   :   LongInt;
 x      :   Integer;
 y      :   Integer;
  
begin
   temp   :=  0;
   rgn  :=  theRegion^^;
   for  x  := rgn.rgnBBox.left  to  rgn.rgnBBox.right do 
        begin
     pt.h := x;
            for y := rgn.rgnBBox.top to rgn.rgnBBox.bottom do
      begin
 pt.v := y;
                if  PtInRgn( pt, TheRegion) then  temp := temp + 1;
 end;
        end;
        CountPix := temp;
end;
{ Turbo seems to accept pt.h and pt.v as control elements but TML does}
{ not. Some format checkers agree with TML}

procedure Wipe;  
var
    r   :   Rect;
      
begin
    SetRect(r,0,0,504,300);
    EraseRect(r);
  
end;

procedure Data;  
var
    rgn         :   Region;
    rgnpntr     :   Ptr;
    size        :   Integer;
    thebuf      :   BUF;
    bfpntr      :   Ptr;
    myString    :   Str255;
    i           :   Integer;
    x           :   Integer;
    y           :   Integer;
 
 begin
    Wipe;
    TextSize(9);
    TextFont(Monaco);
    rgn  :=  totalRegion^^;
    rgnpntr := ptr(totalRegion^); 
    size := rgn.rgnSize;
    if size > 800 then size:= 800;
    bfpntr := ptr(@thebuf);
    BlockMove(rgnpntr,bfpntr,size);
    MoveTo(10,10);
    DrawString(‘Here are the first 400 words of the region data. (FLAG 
= 32767)’);
    x := 10;
    y := 20;
    for i  := 1  to  (size div 2) do 
        begin
        MoveTo(x,y);
        NumToString(theBuf[i],myString);
        if theBuf[i] < 32766 then 
            begin
                if theBuf[i] <10  then DrawString(‘ ‘);
                if theBuf[i] <100 then DrawString(‘ ‘);
                if theBuf[i] < 1000 then DrawString(‘ ‘);
                if theBuf[i] < 10000 then DrawString(‘ ‘);
                DrawString(MyString);
            end;
        if theBuf[i] > 32766 then DrawString(‘ FLAG’);
        x := x + 30;
        if (i mod 16) = 0 then
            begin
            x := 10;
            y := y+10;
            end; 
        end;   
end;

procedure OvalRegion;
var
    RectA : Rect;
      
begin
   Wipe;   
   TotalRegion := NewRgn;
   SetRect(RectA, 170,175,195,200);
   OpenRgn;
   ShowPen;
   FrameOval(RectA);
   HidePen;
   CloseRgn(TotalRegion);   
end;

procedure Contour; 
var
    p1  :   Point;
    p2  :   Point;
    OldTick :  Longint;
    
begin
  Wipe;
  TotalRegion := NewRgn;
  OldTick := TickCount;
  Repeat
    GetMouse(p1);
    MoveTo(p1.h,p1.v);
    p2 := p1;  
  Until Button = True;  
  OpenRgn;
  ShowPen;
  PenMode(patXor);  
  Repeat
    GetMouse(p2);
    Repeat Until (OldTick <> TickCount);
    LineTo(p2.h,p2.v);
  Until Button <> True;  
  Repeat Until (OldTick <> TickCount);
  LineTo(p1.h,p1.v);
  PenNormal;
  HidePen;
  CloseRgn(TotalRegion);
  InvertRgn(TotalRegion);
end;

procedure Example; 
  
begin
    Wipe;
    OpenRgn;
    TotalRegion := NewRgn;
    ShowPen;
    MoveTo(100,100);
    LineTo(200,100);
    LineTo(200,220);
    LineTo(180,220);
    LineTo(180,150);
    LineTo(125,150);
    LineTo(125,170);
    LineTo(125,170);
    LineTo(100,170);
    LineTo(100,100);
    HidePen;
    CloseRgn(TotalRegion);
end;

procedure FreeBox; 
var
    p1  :   Point;
    p2  :   Point;
    p3  :   Point;
    OldTick :  Longint;
    MyRect  :  Rect;
      
begin
    Wipe;
    TotalRegion := NewRgn;
    OldTick := TickCount;
    PenPat(gray);
    PenMode(patXor);    
    Repeat
    GetMouse(p1);
    p2 := p1;  
    Until Button = True;   
    OpenRgn;
    ShowPen;
    PenMode(patXor);    
    Repeat
    Pt2Rect(p1,p2,MyRect);
    Repeat Until (OldTick <> TickCount);
    FrameRect(MyRect);   
        Repeat
            GetMouse(p3);
        Until  EqualPt(p2,p3) <> True;   
   Repeat Until (OldTick <> TickCount);
   FrameRect(MyRect);
   p2 := p3;   
   Until Button <> True;
   Pennormal;
   HidePen;
   PenPat(black);
   FrameRect(MyRect);
   CloseRgn(TotalRegion);
   InvertRgn(TotalRegion);  
end;

procedure Area;  
var
    NumTix  :   LongInt;
    MoreTix :   LongInt;
    TicString   :   Str255;
    PixString   :   Str255;  
  
begin   
   TextFont(Monaco);
   TextSize(9);
   TextMode(0);
   MoveTo(10,20); DrawString(‘ Using Pascal ‘); 
   NumTix := TickCount;
   NumPix :=  CountPix( TotalRegion ); 
   MoreTix := TickCount - NumTix;
   NumToString(MoreTix,TicString);
   NumToString(NumPix,PixString);
   MoveTo(10,30); DrawString(‘ Tickcount = ‘);
   MoveTo(120,30); DrawString(TicString);
   MoveTo(10,40); DrawString(‘ Pixel Number = ‘);
   MoveTo(120,40); DrawString(PixString);    
   MoveTo(10,50); DrawString(‘ Using Tom Terrific ‘); 
   NumTix := TickCount;
   NumPix :=  ACountPix( TotalRegion ); 
   MoreTix := TickCount - NumTix;
   NumToString(MoreTix,TicString);
   NumToString(NumPix,PixString);
   MoveTo(10,60); DrawString(‘ Tickcount = ‘);
   MoveTo(120,60); DrawString(TicString);
   MoveTo(10,70); DrawString(‘ Pixel Number = ‘);
   MoveTo(120,70); DrawString(PixString);  
end;

procedure ProcessMenu(codeWord : Longint);   
var
  menuNum : Integer;
  itemNum : Integer;
 
begin
  if codeWord <> 0 then 
    begin
      menuNum := HiWord(codeWord);
      itemNum := LoWord(codeWord);
      case menuNum of 
        FileMenuID :Done := true; 
        OptionMenuID :
            begin
                case ItemNum of
                    1:Contour;      {Contour}
                    2:FreeBox;      {Freebox}
                    3:OvalRegion;   {Oval}
                    4:Example;      {Example}
                    5: Area;        {Area}
                    6:Data;         {Region Data}
                end; { of ItemNum case}               
       end;{ of MenuNum case}
    end;
  HiliteMenu(0); 
 end;
end;

procedure DealWithMouseDowns(theEvent: EventRecord);
var
  location : Integer;
  windowPointedTo : WindowPtr;
  mouseLoc : point;
  windowLoc : integer;
  VandH : Longint;
  Height : Integer;
  Width : Integer;
  
 begin
  mouseLoc := theEvent.where;
  windowLoc := FindWindow(mouseLoc,windowPointedTo);
  case windowLoc of
    inMenuBar : 
      begin
        ProcessMenu(MenuSelect(mouseLoc));
      end;
    
  end;
end;

procedure MainEventLoop;
var
  Event : EventRecord;
  theItem : integer;
  
begin
  repeat
    SystemTask;
    if GetNextEvent(everyEvent, Event) then
     begin  
      case Event.what of
       mouseDown : DealWithMouseDowns(Event);
      end;
     end;
  until Done;
end;

procedure MakeMenus; 
var
  index : Integer;
begin
  for index := FileMenuId to OptionMenuID do
    begin
      myMenus[index] := GetMenu(index);
      InsertMenu(myMenus[index],0);
    end;
  DrawMenuBar;
end;

{  Main Program   }
begin
  Done := false;   FlushEvents(everyEvent,0);        InitGraf(@thePort);
   InitFonts;    InitWindows;   InitMenus;     InitDialogs(nil);
  InitCursor;  
  MoreMasters;
  MoreMasters;
  MakeMenus;
  MyWindow := GetNewWindow(WindResID,nil,Pointer(-1)); 
  SetPort(MyWindow); 
  TotalRegion := NewRgn;   {Lazy way to avoid bomb if your select “Area” 
first}  
  MainEventLoop; 
end.

Here is the resource file for use with the above program (Turbo Pascal):

*
*   Resource listing from file: “PasArea.R”.
*

PasArea.rsrc

Type AREA = STR 
,0
PasArea, by Stephen Dubin and Thomas W. Moore Copyright © 1987 

Type WIND
,1
Fun with Regions II
40 5 330 505
Visible NoGoAway
0
0

Type MENU
,1
File
Quit

,2
Option
Contour
Freebox
Oval
Example
Compute Area
Region Data

In order to compile the same program with TML Pascal V2.0, a few minor adjustments were needed. The preamble was changed to:

{5}
program TMPasArea;

{$T APPL AREA} { set the type and creator}
{$B+} { set the bundle bit}
{$L TMPasAreaRes}{ link the resource file too...}

uses MacIntf;

{ Constant, Type and Variable declarations as above are the same as in 
PasArea.Pas above}

{Declare the Assembly Language routine as external }
function ACountPix( theRegion:RgnHandle) : LongInt; external;
{$U ACountPix  } 
{ This directive will not appear in the .link file unless it follows 
the declaration of the }
{ relocatable object file as external}

The only change needed in the body of the program was in the high level CountPix function. A form that compiled with TML is:


{6}
function CountPix(theRegion : RgnHandle): LongInt;
var
 pt : Point;
 rgn    :   Region;
 temp   :   LongInt;
 x :   Integer;
 y :   Integer;
  
begin
   temp   :=  0;
   rgn  :=  theRegion^^;
   for  x  := rgn.rgnBBox.left  to  rgn.rgnBBox.right do 
        begin
            pt.h := x;
            for y := rgn.rgnBBox.top to rgn.rgnBBox.bottom do
                begin
                   pt.v := y;
                   if  PtInRgn( pt, TheRegion) then  temp := temp + 1;
                end;
        end;
        CountPix := temp;
end;

TML does not seem to like having pt.h and pt.v as control elements. PasMat, a Pascal formatting and syntax checking program, agrees with TML on this point. In keeping with our local traditions, the first non-comment line of our TML resource file was “TMPasAreaRes”. Although it probably is of little interest in these days of monstrous memories, the TML version of the program requires 3,305 bytes of memory; whereas the Turbo program weighs in at a hefty 10,855 bytes.

Some final zingers for the reader - Although it was certainly necessary for us to use assembler to plumb the depths of the ROM and to work out the algorithm for making our area measurement lightning fast; one might consider whether the same algorithm might now be implemented entirely from C, Pascal or possibly Basic. Would the speed be degraded to any appreciable extent? Will a new call AreaRgn be found in the 512K Roms on the Mack III’s?

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Dragon Dictate 6.0 - Premium voice-recog...
With Dragon Dictate speech recognition software, you can use your voice to create and edit text or interact with your favorite Mac applications. Far more than just speech-to-text, Dragon Dictate lets... Read more
OmniFocus 3.11.7 - GTD task manager with...
OmniFocus is an organizer app. It uses projects to organize tasks naturally, and then add tags to organize across projects. Easily enter tasks when you’re on the go, and process them when you have... Read more
rekordbox 6.5.1.0009 - Professional DJ m...
rekordbox is the best way of preparing and managing your tracks, be it at home, in the studio, or even on the plane! It allows you to import music from other music-management software using the... Read more
1Password 7.8.1 - Powerful password mana...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
Ableton Live 10.1.35 - Record music usin...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
Microsoft Office 365, 2019 16.48 - Popul...
Microsoft Office 365. The essentials to get it all done. Unmistakably Office, designed for Mac Get started quickly with new, modern versions of Word, Excel, PowerPoint, Outlook and OneNote-... Read more
Adobe After Effects 18.1 - Create profes...
After Effects is available as part of Adobe Creative Cloud for $52.99/month (or $20.99/month for a single app license). The new, more connected After Effects can make the impossible possible. Get... Read more
Adobe Audition 14.1 - Professional post-...
Audition is available as part of Adobe Creative Cloud for as little as $20.99/month (or $9.99/month if you're a previous Audition customer). Adobe Audition empowers you to create and deliver... Read more
Adobe Animate 21.0.5 - Animation authori...
Animate is available as part of Adobe Creative Cloud for as little as $20.99/month (or $9.99/month if you're a previous Flash Professional customer). Animate (was Flash CC) lets you share work... Read more
Adobe Photoshop 22.3.1 - Professional im...
You can download Photoshop for Mac as a part of Creative Cloud for only $20.99/month (or $9.99/month if you have purchased an earlier software version). Adobe Photoshop is a recognized classic of... Read more

Latest Forum Discussions

See All

Moonlight Sculptor is an upcoming MMORPG...
Kakao Games and XL Games – who you might be familiar with from their previous game ArcheAge – have announced that their MMORPG Moonlight Sculptor is now available to pre-order for iOS and Android devices. Moonlight Sculptor has previously launched... | Read more »
MU Archangel is now open for pre-registr...
MU Archangel is now open for pre-registration in Southeast Asia following its massive success in other territories. Players from Singapore, Thailand, Malaysia, Indonesia, and the Philippines (except Vietnam) can now join in on the fun by applying... | Read more »
Compete, a new social media app you can...
Whoever told you you can’t get rich making videos has obviously never heard of Compete, Competitive Media Technologies Limited’s hot new social media app where you can rake in all the dough just by doing what you love. Video monetization that... | Read more »
Bethesda has released a new DOOM mobile...
Bethesda Softworks has released a new DOOM game out of the blue exclusively for mobile devices. It’s called Mighty DOOM and is currently only available as an early access title on Android but will be expanding to more users in the future. [Read... | Read more »
Anagraphs is a word puzzle game with a t...
Cinq-Mars Media has released its word puzzle game Anagraphs for iOS and Android devices. The game released last week after a short delay in getting it onto the appropriate platforms. [Read more] | Read more »
These are the top 5 best iPhone games li...
Fortnite has been the big hitter in mobile gaming this year, and it's not hard to see why. Thanks to some excellent marketing, and a polished experience that almost anyone can enjoy, it's really taken the App Store by storm. But there are other... | Read more »
The top 5 best iPhone games like Pokemon...
Pokemon GO is still the, if you'll excuse the pun, go-to game if you want some AR action on your phone. But it's not the only choice out there, and if you've got a hankering for something a bit different, then your eyes might already have started... | Read more »
The top 5 best iPhone games like Starcra...
Starcraft sits at the top of the RTS tree for a number of very good reasons. It also isn't on mobile, again, for a number of very good reasons. But that doesn't mean you can't find a way to indulge your sci-fi, competitive, massive, or engaging RTS... | Read more »
Apple Arcade: Ranked - Top 25 [Updated 4...
In case you missed it, I am on a quest to rank every Apple Arcade game there is. [Read more] | Read more »
The top 5 best iPhone games like The Roo...
The Room has had a massive impact on the world of mobile gaming. Not only is it a brilliant adventure, it also shows how the touchscreen controls on your iPhone can be turned into something far more elegant and tactile than just a bunch of buttons... | Read more »

Price Scanner via MacPrices.net

B&H is offering clearance prices on lefto...
Apple reseller B&H Photo has clearance 2020 13″ 1.4GHz Intel-based MacBook Pros on sale today for $200-$300 off Apple’s original MSRP with prices starting at only $1099. Expedited shipping is... Read more
Roundup of Today’s Best MacBook Deals: M1 Mac...
Apple resellers are offering sale prices on Apple’s M1-powered 13″ MacBook Airs ranging up to $190 off MSRP. Here’s where to pick one up today, and as always, keep an eye on our 13″ MacBook Air Price... Read more
Apple AirPods Pro drop to new low price of on...
Amazon has Apple’s AirPods Pro on sale today for a new low price of only $197 shipped. That’s $52 off MSRP and the lowest price currently available for a set of AirPods Pro from any Apple reseller.... Read more
Apple restocks clearance 13″ Intel-based MacB...
Apple has clearance, Certified Refurbished, 2020 13″ Intel-based MacBook Airs available starting at only $809 and up to $280 off original MSRP. Each MacBook features a new outer case, comes with a... Read more
OWC drops prices on 2020 Intel multi-core Mac...
Other World Computing has clearance 2020 Intel-based Mac minis on sale starting at only $499. Both 4-core and 6-core models are in stock today. These are new, unopened, factory-sealed minis: – 3.6GHz... Read more
Save $50 off Apple’s 10.9″ iPad Air today at...
B&H Photo has new 10.9″ Apple iPad Airs in stock and on sale today for up to $50 off MSRP. Expedited shipping is free to most addresses in the US. Note that some sale prices may be restricted to... Read more
Rare Apple sale: Get a HomePod mini for $10 o...
Apple reseller Expercom has the Space Gray HomePod mini on sale today for $89 shipped. Their price is $10 off Apple’s MSRP, and it’s currently the only sale price available for a HomePod mini among... Read more
Apple has M1 Mac minis available starting at...
Apple has a full line of standard configuration M1 Mac minis available in their Certified Refurbished section starting at only $589 and up to $140 off MSRP. Each mini comes with Apple’s one-year... Read more
New sale at Amazon: $55-$70 discounts on Appl...
Amazon has 7.9″ iPad minis on sale today for up to $70 off Apple’s MSRP, each including free shipping. Prices start at $344. These are the same iPad minis sold by Apple in their retail and online... Read more
Apple offering 13″ M1 MacBook Airs for as lit...
Apple has a full line of 2020 13″ M1 MacBook Airs available, Certified Refurbished, starting at only $849 and up to $190 off original MSRP. These are the cheapest M1 MacBook Airs for sale today at... Read more

Jobs Board

*Apple* Valley 20hr Teller - Wells Fargo (Un...
…or scheduled + Ability to stand for extended periods of time **Street Address** **MN- Apple Valley:** 14325 Cedar Ave - Apple Valley, MN **Disclaimer** All offers Read more
*Apple* Valley 20hr Teller - Wells Fargo (Un...
…or scheduled + Ability to stand for extended periods of time **Street Address** **MN- Apple Valley:** 14325 Cedar Ave - Apple Valley, MN **Disclaimer** All offers Read more
Desktop Support Technician - *Apple* / Mac...
…infrastructure and internal desktop systems. Must have an IT background that includes Apple / Mac support. **Overview:** + Responds to routine technical questions or Read more
Geek Squad *Apple* Consultation Professiona...
**801042BR** **Job Title:** Geek Squad Apple Consultation Professional **Job Category:** Store Associates **Store Number or Department:** 000214-Willowbrook-Store Read more
*Apple* Mobility Specialist - Best Buy (Unit...
**800879BR** **Job Title:** Apple Mobility Specialist **Job Category:** Store Associates **Store Number or Department:** 000803-Lansing-Store **Job Description:** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.