home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Snippets / PNL Libraries / NSSprites.p < prev    next >
Encoding:
Text File  |  1997-05-29  |  25.6 KB  |  844 lines  |  [TEXT/CWIE]

  1. unit NSSprites;
  2.  
  3. interface
  4.  
  5.     uses
  6.         Types, Quickdraw, QDOffscreen;
  7.  
  8. {
  9.     Required colour quickdraw
  10.     All internal worlds are 8-bit
  11. }
  12.  
  13. {$ifc undefined useNSBlitter}
  14. {$setc useNSBlitter := 1}
  15. {$endc}
  16.  
  17. {$ifc not GENERATINGCFM}            
  18.     var
  19.         needSwapMMUMode: boolean;
  20. {$endc}
  21.  
  22.     const
  23.         max_sprite_size = 32;
  24.         max_sprite_area_size = 1280;
  25.         grid_size = max_sprite_size;
  26.         max_grid_size = max_sprite_area_size div max_sprite_size;
  27.         
  28.     const
  29.         change_rect_max = 200;
  30.         kNoSprite = -1;
  31.     
  32.     type
  33.         BlitterProc = procedure( sprite_refcon: longint; source: NSFramePtr; const source_rect, mask_rect: Rect; dest: NSFramePtr; const dest_rect: Rect );
  34.         
  35.         NSList = record
  36.             next: NSListPtr;
  37.             prev: NSListPtr;
  38.             head: boolean;
  39.         end;
  40.         NSListPtr = ^NSList;
  41.  
  42.         NSWorld = record
  43.             background: NSFramePtr;
  44.             work: NSFramePtr;
  45.             sprite: NSFramePtr;
  46.             window: NSFramePtr;
  47.             sprites: NSSpritePtr;
  48.             offscreen_blitter: BlitterProc;
  49.             change_rects: array[1..change_rect_max] of Rect;
  50.             change_rect_count: longint;
  51.             grid: array[-1..max_grid_size-1, -1..max_grid_size-1] of NSList;
  52.         end;
  53.         NSWorldPtr = ^NSWorld;
  54.         
  55.         NSFrame = record
  56.             world: GWorldPtr;
  57.             pixMap: PixMapPtr;
  58.             frameBaseAddr: Ptr;
  59.             rowBytes: longint;
  60.             bounds: Rect;
  61.             dispose_gworld: boolean;
  62.         end;
  63.         NSFramePtr = ^NSFrame;
  64.         
  65.         NSSprite = record
  66.             grid: NSList; { WARNING: fix up sprite_to_grid_offset if this moves! }
  67.             next: NSSpritePtr;
  68.             world: NSWorldPtr;
  69.             source_rect: Rect;
  70.             mask_rect: Rect;
  71.             dest_rect: Rect;
  72.             blitter: BlitterProc;
  73.             refcon: longint;
  74.             visible: boolean;
  75.         end;
  76.         NSSpritePtr = ^NSSprite;
  77.         
  78.         NSSavedPort = record
  79.             world: GWorldPtr;
  80.             device: GDHandle;
  81.         end;
  82.         
  83.     const
  84.         sprite_to_grid_offset = 0;
  85.         
  86.     const
  87.         kSpriteAfterNone = nil; { cheap }
  88.         kSpriteAfterAll = NSSpritePtr(-1); { expensive }
  89.     
  90.     procedure NSInitSprites;
  91.  
  92.     function NSCreateWorld( var world: NSWorldPtr; window: WindowPtr; const window_bounds, sprite_frame: Rect ): OSStatus;
  93.     procedure NSDestroyWorld( var world: NSWorldPtr );
  94.     function NSResize( var world: NSWorldPtr; const newframe: Rect ): OSStatus;
  95.         { WARNING: If NSResize fails, it destroys the NSWorld! }
  96.     
  97.     procedure NSGetGWorld( var saved: NSSavedPort );
  98.     procedure NSSetGWorld( world: GWorldPtr; var saved: NSSavedPort );
  99.     procedure NSRestoreGWorld( const saved: NSSavedPort );
  100.         
  101.     procedure NSAddChangeRect( world: NSWorldPtr; changeRect: Rect );
  102.         { call this if you change the background }
  103.         
  104.     procedure NSAnimate( world: NSWorldPtr );
  105.     procedure NSUpdate( world: NSWorldPtr; update: Rect );
  106.     
  107.     function NSCreateSprite( var sprite: NSSpritePtr; world: NSWorldPtr; const source_rect, mask_rect: Rect ): OSStatus; // after: NSSpritePtr; 
  108.     procedure NSDestroySprite( var sprite: NSSpritePtr );
  109.     
  110.     procedure NSSetSpriteVisible( sprite: NSSpritePtr; visible: boolean );
  111.     procedure NSSetSpriteLocation( sprite: NSSpritePtr; h, v: integer );
  112.     procedure NSOffsetSpriteLocation( sprite: NSSpritePtr; dh, dv: integer );
  113.     procedure NSSetSpriteBlitter( sprite: NSSpritePtr; blitter: BlitterProc );
  114.     procedure NSSetSpriteSource( sprite: NSSpritePtr; const source_rect, mask_rect: Rect );
  115.     procedure NSSetSpriteRefcon( sprite: NSSpritePtr; refcon: longint );
  116.     
  117.     procedure NSSetPortToBackground( world: NSWorldPtr );
  118.     procedure NSSetPortToWork( world: NSWorldPtr );
  119.     procedure NSSetPortToSprite( world: NSWorldPtr );
  120.     procedure NSSetPortToWindow( world: NSWorldPtr );
  121.  
  122.     procedure NSCopyBitsRect( source_world: GWorldPtr; const source_rect: Rect; dest_world: GWorldPtr; const dest_rect: Rect );
  123.  
  124.     procedure NSCopyBitsNoMaskBlitter( refcon: longint; source: NSFramePtr; const source_rect, mask_rect: Rect; dest: NSFramePtr; const dest_rect: Rect );
  125.     procedure NSBlitter8BitNoMaskDrawProc( refcon: longint; source: NSFramePtr; const source_rect, mask_rect: Rect; dest: NSFramePtr; const dest_rect: Rect );
  126.  
  127.     function InsetSource( const within: Rect; var dest, source, mask: Rect ): boolean;
  128.     function WithinRect( const within, bounds: Rect ): boolean;
  129.  
  130. implementation
  131.  
  132.     uses
  133.         Memory, OSUtils, 
  134. {$ifc useNSBlitter}
  135.         NSSpriteWorld8BitBlitters, 
  136. {$endc}
  137.         MyMemory, MyAssertions, MyUtils, MyLowLevel, MyMathUtils;
  138.  
  139. {$setc hack_show_drawing := 0}
  140.     
  141.     var
  142.         null_rect: Rect;
  143. {$ifc hack_show_drawing}
  144.         hack_world: NSWorldPtr;
  145. {$endc}
  146.         
  147.     procedure NSCopyBitsRect( source_world: GWorldPtr; const source_rect: Rect; dest_world: GWorldPtr; const dest_rect: Rect );
  148.     begin
  149.         Assert( source_rect.right - source_rect.left = dest_rect.right - dest_rect.left );
  150.         Assert( source_rect.bottom - source_rect.top = dest_rect.bottom - dest_rect.top );
  151.  
  152.         CopyBits( GrafPtr(source_world)^.portBits, GrafPtr(dest_world)^.portBits, source_rect, dest_rect, srcCopy, nil );
  153.     end;
  154.     
  155.     procedure NSCopyBitsNoMaskBlitter( refcon: longint; source: NSFramePtr; const source_rect, mask_rect: Rect; dest: NSFramePtr; const dest_rect: Rect );
  156.     begin
  157. {$unused( mask_rect, refcon )}
  158.         Assert( source <> nil );
  159.         Assert( dest <> nil );
  160.         NSCopyBitsRect( source^.world, source_rect, dest^.world, dest_rect );
  161.     end;
  162.     
  163. {$ifc useNSBlitter}
  164.     procedure NSBlitter8BitNoMaskDrawProc( refcon: longint; source: NSFramePtr; const source_rect, mask_rect: Rect; dest: NSFramePtr; const dest_rect: Rect );
  165. {$ifc not GENERATINGCFM}        
  166.         var    
  167.             mmuMode: SignedByte;
  168. {$endc}
  169.     begin
  170. {$unused( refcon,mask_rect )}
  171.         Assert( (source <> nil) & (dest <> nil) );
  172.         Assert( WithinRect( dest_rect, dest^.bounds ) );
  173.  
  174. {$ifc not GENERATINGCFM}            
  175.         if needSwapMMUMode then begin
  176.             mmuMode := true32b;
  177.             SwapMMUMode( mmuMode );
  178.         end;
  179. {$endc}
  180.  
  181.         BlitPixie8Bit( 
  182.             PixelChunkPtr( AddPtrLong( source^.frameBaseAddr, source^.rowBytes * source_rect.top + source_rect.left ) ),
  183.             PixelChunkPtr( AddPtrLong( dest^.frameBaseAddr, dest^.rowBytes * dest_rect.top + dest_rect.left ) ),
  184.             dest_rect.bottom - dest_rect.top,
  185.             dest_rect.right - dest_rect.left,
  186.             source^.rowBytes,
  187.             dest^.rowBytes );
  188.             
  189. {$ifc not GENERATINGCFM}            
  190.         if needSwapMMUMode then begin
  191.             SwapMMUMode( mmuMode );
  192.         end;
  193. {$endc}
  194.             
  195.     end;
  196. {$elsec}
  197.     procedure NSBlitter8BitNoMaskDrawProc( refcon: longint; source: NSFramePtr; const source_rect, mask_rect: Rect; dest: NSFramePtr; const dest_rect: Rect );
  198.     begin
  199. {$unused( mask_rect, refcon )}
  200.         Assert( source <> nil );
  201.         Assert( dest <> nil );
  202.         NSCopyBitsRect( source^.world, source_rect, dest^.world, dest_rect );
  203.     end;
  204.     
  205. {$endc}
  206.     
  207.     function InsetSource( const within: Rect; var dest, source, mask: Rect ): boolean;
  208.     begin
  209.         if within.top > dest.top then begin
  210.             source.top := source.top + within.top - dest.top;
  211.             mask.top := mask.top + within.top - dest.top;
  212.             dest.top := within.top;
  213.         end;
  214.         if within.left > dest.left then begin
  215.             source.left := source.left + within.left - dest.left;
  216.             mask.left := mask.left + within.left - dest.left;
  217.             dest.left := within.left;
  218.         end;
  219.         if within.bottom < dest.bottom then begin
  220.             source.bottom := source.bottom + within.bottom - dest.bottom;
  221.             mask.bottom := mask.bottom + within.bottom - dest.bottom;
  222.             dest.bottom := within.bottom;
  223.         end;
  224.         if within.right < dest.right then begin
  225.             source.right := source.right + within.right - dest.right;
  226.             mask.right := mask.right + within.right - dest.right;
  227.             dest.right := within.right;
  228.         end;
  229.         InsetSource := (dest.left < dest.right) & (dest.top < dest.bottom);
  230.     end;
  231.     
  232.     function WithinRect( const within, bounds: Rect ): boolean;
  233.     begin
  234.         WithinRect := (bounds.left <= within.left) & (within.right <= bounds.right) &
  235.                 (bounds.top <= within.top) & (within.bottom <= bounds.bottom);
  236.     end;
  237.     
  238. {$ifc hack_show_drawing}
  239.     procedure HackShowDrawing( dest_frame: NSFramePtr; dest_rect: Rect );
  240.         var
  241.             saved: NSSavedPort;
  242.     begin
  243.         NSSetGWorld( hack_world^.window^.world, saved );
  244.         if dest_frame <> hack_world^.window then begin
  245.             OffsetRect( dest_rect, hack_world^.window^.bounds.left, hack_world^.window^.bounds.top );
  246.         end;
  247.         if odd(Random) then InvertRect( dest_rect );
  248.         NSRestoreGWorld( saved );
  249.     end;
  250.     
  251. {$endc}
  252.     procedure CallBlitter( blitter: BlitterProc; refcon: longint; source_frame: NSFramePtr; const source_rect, mask_rect: Rect; dest_frame: NSFramePtr; const dest_rect, within_rect: Rect );
  253.         var
  254.             dest, within, source, mask: Rect;
  255.     begin
  256.         if SectRect( dest_frame^.bounds, within_rect, within ) then begin
  257.             dest := dest_rect;
  258.             source := source_rect;
  259.             mask := mask_rect;
  260.             if InsetSource( within, dest, source, mask ) then begin
  261.                 blitter( refcon, source_frame, source, mask, dest_frame, dest );
  262. {$ifc hack_show_drawing}
  263.                 HackShowDrawing( dest_frame, dest );
  264. {$endc}
  265.             end;
  266.         end;
  267.     end;
  268.  
  269.     procedure NSGetGWorld( var saved: NSSavedPort );
  270.     begin
  271.         GetGWorld( saved.world, saved.device );
  272.     end;
  273.  
  274.     procedure NSSetGWorld( world: GWorldPtr; var saved: NSSavedPort );
  275.     begin
  276.         GetGWorld( saved.world, saved.device );
  277.         SetGWorld( world, nil );
  278.     end;
  279.     
  280.     procedure NSRestoreGWorld( const saved: NSSavedPort );
  281.     begin
  282.         SetGWorld( saved.world, saved.device );
  283.     end;
  284.  
  285.     function NSCreateFrame( var frame: NSFramePtr; const bounds: Rect ): OSStatus;
  286.         var
  287.             err: OSStatus;
  288.             pm: PixMapHandle;
  289.             dummy: boolean;
  290.             world: GWorldPtr;
  291.             saved_world: NSSavedPort;
  292.     begin
  293.         Assert( (bounds.left < bounds.right) & (bounds.top < bounds.bottom) );
  294.         
  295.         frame := nil;
  296.         
  297.         err := NewGWorld( world, 8, bounds, nil, nil, 0 );
  298.         if err = noErr then begin
  299.             err := MNewPtr( frame, SizeOf(NSFrame) );
  300.             if err <> noErr then begin
  301.                 DisposeGWorld( world );
  302.             end;
  303.         end;
  304.         if err = noErr then begin
  305.             NSSetGWorld( world, saved_world );
  306.             
  307.             pm := GetGWorldPixMap( world );
  308.             Assert( pm <> nil );
  309.             dummy := LockPixels( pm );
  310.             Assert( dummy );
  311.             
  312.             frame^.world := world;
  313.             frame^.pixMap := PixMapPtr(StripAddress(Handle(pm)^));
  314.             frame^.frameBaseAddr := GetPixBaseAddr( pm );
  315.             frame^.rowBytes := pm^^.rowBytes and $7FFF;
  316.             frame^.bounds := bounds;
  317.             frame^.dispose_gworld := true;
  318.             
  319.             EraseRect( bounds );
  320.             FillRect( bounds, qd.ltGray );
  321.             
  322.             NSRestoreGWorld( saved_world );
  323.         end;
  324.         NSCreateFrame := err;
  325.     end;
  326.     
  327.     function NSCreateFrameFromWindow( var frame: NSFramePtr; window: WindowPtr; const bounds: Rect ): OSStatus;
  328.         var
  329.             err: OSStatus;
  330.             pm: PixMapHandle;
  331.             dummy: boolean;
  332.             saved: NSSavedPort;
  333.             window_gworld: GWorldPtr;
  334.             window_device: GDHandle;
  335.     begin
  336.         Assert( (bounds.left < bounds.right) & (bounds.top < bounds.bottom) );
  337.         
  338.         frame := nil;
  339.         
  340.         NSGetGWorld( saved );
  341.         
  342.         SetPort( window );
  343.         GetGWorld( window_gworld, window_device );
  344.         
  345.         err := MNewPtr( frame, SizeOf(NSFrame) );
  346.         if err = noErr then begin            
  347.             pm := GetGWorldPixMap( window_gworld );
  348.             Assert( pm <> nil );
  349.             dummy := LockPixels( pm );
  350.             Assert( dummy );
  351.             
  352.             frame^.world := window_gworld;
  353.             frame^.pixMap := PixMapPtr(StripAddress(Handle(pm)^));
  354.             frame^.frameBaseAddr := GetPixBaseAddr( pm );
  355.             frame^.rowBytes := pm^^.rowBytes and $7FFF;
  356.             frame^.bounds := bounds;
  357.             frame^.dispose_gworld := false;
  358.         end;
  359.  
  360.         NSRestoreGWorld( saved );
  361.  
  362.         NSCreateFrameFromWindow := err;
  363.     end;
  364.     
  365.     procedure NSDestroyFrame( var frame: NSFramePtr );
  366.     begin
  367.         if frame <> nil then begin
  368.             Assert( frame^.world <> nil );
  369.             if frame^.dispose_gworld then begin
  370.                 DisposeGWorld( frame^.world );
  371.             end;
  372.             MDisposePtr( frame );
  373.         end;
  374.     end;
  375.     
  376.     procedure AddSpriteToGrid( sprite: NSSpritePtr );
  377.         var
  378.             x, y: integer;
  379.     begin
  380.         x := sprite^.dest_rect.left div grid_size;
  381.         if (-1 <= x) & (x < max_grid_size) then begin
  382.             y := sprite^.dest_rect.top div grid_size;
  383.             if (-1 <= y) & (y < max_grid_size) then begin
  384.                 sprite^.grid.next := sprite^.world^.grid[ x, y ].next;
  385.                 sprite^.grid.prev := @sprite^.world^.grid[ x, y ];
  386.                 sprite^.world^.grid[ x, y ].next^.prev := @sprite^.grid;
  387.                 sprite^.world^.grid[ x, y ].next := @sprite^.grid;
  388.             end;
  389.         end;
  390.     end;
  391.     
  392.     procedure RemoveSpriteFromGrid( sprite: NSSpritePtr );
  393.     begin
  394.         if sprite^.grid.next <> nil then begin
  395.             sprite^.grid.next^.prev := sprite^.grid.prev;
  396.             sprite^.grid.prev^.next := sprite^.grid.next;
  397.             sprite^.grid.prev := nil;
  398.             sprite^.grid.next := nil;
  399.         end;
  400.     end;
  401.     
  402.     procedure ResetGrid( world: NSWorldPtr );
  403.         var
  404.             x, y: integer;
  405.             sprite: NSSpritePtr;
  406.     begin
  407.         for x := -1 to max_grid_size - 1 do begin
  408.             for y := -1 to max_grid_size - 1 do begin
  409.                 world^.grid[ x, y ].next := @world^.grid[ x, y ];
  410.                 world^.grid[ x, y ].prev := @world^.grid[ x, y ];                    
  411.                 world^.grid[ x, y ].head := true;                    
  412.             end;
  413.         end;
  414.         sprite := world^.sprites;
  415.         while sprite <> nil do begin
  416.             sprite^.grid.next := nil;
  417.             sprite^.grid.prev := nil;
  418.             sprite^.grid.head := false;
  419.             if sprite^.visible then begin
  420.                 AddSpriteToGrid( sprite );
  421.             end;
  422.         end;
  423.     end;
  424.     
  425.     function NSCreateWorld( var world: NSWorldPtr; window: WindowPtr; const window_bounds, sprite_frame: Rect ): OSStatus;
  426.         var
  427.             err: OSStatus;
  428.             bounds: Rect;
  429.     begin
  430.         Assert( window <> nil );
  431.         Assert( (window_bounds.left < window_bounds.right) & (window_bounds.top < window_bounds.bottom) );
  432.         Assert( (sprite_frame.left = 0) & (sprite_frame.top = 0) );
  433.         Assert( (sprite_frame.left < sprite_frame.right) & (sprite_frame.top < sprite_frame.bottom) );
  434.         Assert( RectWidth( window_bounds ) <= max_sprite_area_size );
  435.         Assert( RectHeight( window_bounds ) <= max_sprite_area_size );
  436.         
  437.         SetRect( null_rect, 0, 0, 0, 0 );
  438.         
  439.         err := MNewPtr( world, SizeOf(NSWorld) );
  440.         if err = noErr then begin
  441. {$ifc hack_show_drawing}
  442.             hack_world := world;
  443. {$endc}
  444.             world^.sprites := nil;
  445.             ResetGrid( world );
  446.             world^.change_rect_count := 0;
  447.             world^.offscreen_blitter := NSBlitter8BitNoMaskDrawProc;
  448.             
  449.             SetRect( bounds, 0, 0, window_bounds.right - window_bounds.left, window_bounds.bottom - window_bounds.top );
  450.             
  451.             AddOSStatus( err, NSCreateFrameFromWindow( world^.window, window, window_bounds ) );
  452.             AddOSStatus( err, NSCreateFrame( world^.background, bounds ) );
  453.             AddOSStatus( err, NSCreateFrame( world^.work, bounds ) );
  454.             AddOSStatus( err, NSCreateFrame( world^.sprite, sprite_frame ) );
  455.             
  456.             if err <> noErr then begin
  457.                 NSDestroyFrame( world^.window );
  458.                 NSDestroyFrame( world^.background );
  459.                 NSDestroyFrame( world^.work );
  460.                 NSDestroyFrame( world^.sprite );
  461.             end;
  462.         end;
  463.         
  464.         NSCreateWorld := err;
  465.     end;
  466.  
  467.     procedure NSDestroyWorld( var world: NSWorldPtr );
  468.         var
  469.             sprite, tmpsprite: NSSpritePtr;
  470.     begin
  471.         if world <> nil then begin
  472.  
  473.             sprite := world^.sprites;
  474.             while sprite <> nil do begin
  475.                 tmpsprite := sprite;
  476.                 sprite := sprite^.next;
  477.                 NSDestroySprite( tmpsprite );
  478.             end;
  479.  
  480.             NSDestroyFrame( world^.window );
  481.             NSDestroyFrame( world^.background );
  482.             NSDestroyFrame( world^.work );
  483.             NSDestroyFrame( world^.sprite );
  484.             MDisposePtr( world );
  485.         end;
  486.     end;
  487.     
  488.     function NSResize( var world: NSWorldPtr; const newframe: Rect ): OSStatus;
  489.         var
  490.             err: OSStatus;
  491.             bounds: Rect;
  492.     begin
  493.         Assert( world <> nil );
  494.         Assert( RectWidth( world^.window^.bounds ) <= max_sprite_area_size );
  495.         Assert( RectHeight( world^.window^.bounds ) <= max_sprite_area_size );
  496.         
  497.         err := noErr;
  498.         
  499.         if (RectWidth( world^.window^.bounds ) <> RectWidth( newframe )) | (RectHeight( world^.window^.bounds ) <> RectHeight( newframe )) then begin
  500.         
  501.             NSDestroyFrame( world^.background );
  502.             NSDestroyFrame( world^.work );
  503.             
  504.             SetRect( bounds, 0, 0, RectWidth( newframe ), RectHeight( newframe ) );
  505.             
  506.             AddOSStatus( err, NSCreateFrame( world^.background, bounds ) );
  507.             AddOSStatus( err, NSCreateFrame( world^.work, bounds ) );
  508.         
  509.         end;
  510.  
  511.         world^.window^.bounds := newframe;
  512.             
  513.         if err <> noErr then begin
  514.             NSDestroyWorld( world );
  515.         end;
  516.         
  517.         NSResize := err;
  518.     end;
  519.     
  520.     procedure NSAddChangeRect( world: NSWorldPtr; changeRect: Rect );
  521.         label
  522.             again;
  523.         var
  524.             index: integer;
  525.             changedP: RectPtr;
  526.     begin
  527.         Assert( world <> nil );
  528.         if (changeRect.left < changeRect.right) & (changeRect.top < changeRect.bottom) then begin
  529.  
  530. again:
  531.             if world^.change_rect_count > 0 then begin
  532.                 changedP := @world^.change_rects;
  533.                 for index := 1 to world^.change_rect_count do begin
  534.                     { check for changeRect entirely contained inside changedP }
  535.                     if (changedP^.left <= changeRect.left) & (changedP^.top <= changeRect.top) &
  536.                             (changedP^.right >= changeRect.right) & (changedP^.bottom >= changeRect.bottom) then begin
  537.                         Exit(NSAddChangeRect);
  538.                     end;
  539.  
  540.                     OffsetPtr( changedP, SizeOf(Rect) );
  541.                 end;
  542.                 
  543.                 changedP := @world^.change_rects;
  544.                 for index := 1 to world^.change_rect_count do begin
  545.                     { check for changeRect vertically adjacent to changedP }
  546.                     if (changedP^.left = changeRect.left) & (changedP^.right = changeRect.right) then begin
  547.                         if ((changedP^.top <= changeRect.bottom) & (changeRect.bottom <= changedP^.bottom)) |
  548.                                 ((changeRect.top <= changedP^.bottom) & (changedP^.bottom <= changeRect.bottom)) then begin
  549.                             changedP^.top := Min( changedP^.top, changeRect.top );
  550.                             changedP^.bottom := Max( changedP^.bottom, changeRect.bottom );
  551.  
  552.                             changeRect := changedP^;
  553.                             changedP^ := world^.change_rects[world^.change_rect_count];
  554.                             Dec( world^.change_rect_count );
  555.                             goto again;
  556.                         end;
  557.                     end;
  558.  
  559.                     { check for changeRect horizontally adjacent to changedP }
  560.                     if (changedP^.top = changeRect.top) & (changedP^.bottom = changeRect.bottom) then begin
  561.                         if ((changedP^.left <= changeRect.right) & (changeRect.right <= changedP^.right)) |
  562.                                 ((changeRect.left <= changedP^.right) & (changedP^.right <= changeRect.right)) then begin
  563.                             changedP^.left := Min( changedP^.left, changeRect.left );
  564.                             changedP^.right := Max( changedP^.right, changeRect.right );
  565.  
  566.                             changeRect := changedP^;
  567.                             changedP^ := world^.change_rects[world^.change_rect_count];
  568.                             Dec( world^.change_rect_count );
  569.                             goto again;
  570.                         end;
  571.                     end;
  572.                     
  573.                     OffsetPtr( changedP, SizeOf(Rect) );
  574.                 end;
  575.                 
  576.                 changedP := @world^.change_rects;
  577.                 index := 1;
  578.                 while index <= world^.change_rect_count do begin
  579.                     { check for changedP entirely contained by changeRect }
  580.                     if (changedP^.left >= changeRect.left) & (changedP^.top >= changeRect.top) &
  581.                             (changedP^.right <= changeRect.right) & (changedP^.bottom <= changeRect.bottom) then begin
  582.                         changedP^ := world^.change_rects[world^.change_rect_count];
  583.                         Dec( world^.change_rect_count );
  584.                     end else begin
  585.                         Inc(index);
  586.                         OffsetPtr( changedP, SizeOf(Rect) );
  587.                     end;
  588.                 end;
  589.             end;
  590.             
  591.             Assert( world^.change_rect_count < change_rect_max );
  592.             Inc(world^.change_rect_count);
  593.             world^.change_rects[world^.change_rect_count] := changeRect;
  594.         end;
  595.     end;
  596.     
  597.     function IntersectRect( const r1, r2: Rect ): boolean;
  598.     begin
  599.         Assert( (r1.left < r1.right) & (r1.top < r1.bottom) );
  600.         Assert( (r2.left < r2.right) & (r2.top < r2.bottom) );
  601.         
  602.         IntersectRect := not ((r2.right <= r1.left) | (r1.right <= r2.left) | (r2.bottom <= r1.top) | (r1.bottom <= r2.top));
  603.     end;
  604.  
  605.     procedure DoUpdate( world: NSWorldPtr; update: Rect );
  606.         var
  607.             display: Rect;
  608.             blitter: BlitterProc;
  609.     begin
  610.         NSSetPortToWindow( world );
  611.         display := update;
  612.         OffsetRect( display, world^.window^.bounds.left, world^.window^.bounds.top );
  613.         ShieldCursor( display, world^.window^.world^.portPixMap^^.bounds.topleft );
  614.         blitter := NSCopyBitsNoMaskBlitter;
  615.         if world^.window^.pixMap^.pixelSize = 8 then begin
  616.             blitter := NSBlitter8BitNoMaskDrawProc;
  617.         end;
  618.         CallBlitter( blitter, kNoSprite, world^.work, update, null_rect, world^.window, display, world^.window^.bounds );
  619.         ShowCursor;
  620.     end;
  621.     
  622.     procedure NSUpdate( world: NSWorldPtr; update: Rect );
  623.     begin
  624.         OffsetRect( update, -world^.window^.bounds.left, -world^.window^.bounds.top );
  625.         DoUpdate( world, update );
  626.     end;
  627.     
  628.     procedure NSAnimateRect( world: NSWorldPtr; const changed: Rect );
  629.         var
  630.             sprite: NSSpritePtr;
  631.             grid: NSListPtr;
  632.             x, y, x1, y1, x2, y2: integer;
  633.     begin
  634.         NSSetPortToWork( world );
  635.         CallBlitter( world^.offscreen_blitter, kNoSprite, world^.background, changed, null_rect, world^.work, changed, changed );
  636.  
  637.         x1 := Max( -1, changed.left div grid_size - 1 );
  638.         x2 := Min( max_grid_size - 1, changed.right div grid_size );
  639.         y1 := Max( -1, changed.top div grid_size - 1 );
  640.         y2 := Min( max_grid_size - 1, changed.bottom div grid_size );
  641.         for y := y1 to y2 do begin
  642.             for x := x1 to x2 do begin
  643.                 grid := world^.grid[ x, y ].next;
  644.                 while not grid^.head do begin
  645.                     sprite := NSSpritePtr( longint(grid) - sprite_to_grid_offset );
  646.                     if IntersectRect( changed, sprite^.dest_rect ) then begin
  647.                         CallBlitter( sprite^.blitter, sprite^.refcon, world^.sprite, sprite^.source_rect, sprite^.mask_rect, world^.work, sprite^.dest_rect, changed );
  648.                     end;
  649.                     grid := grid^.next;
  650.                 end;
  651.             end;
  652.         end;
  653.  
  654.         DoUpdate( world, changed );
  655.     end;
  656.         
  657.     procedure NSAnimate( world: NSWorldPtr );
  658.         var
  659.             i: integer;
  660.     begin
  661.         Assert( world <> nil );
  662.         
  663.         for i := 1 to world^.change_rect_count do begin
  664.             NSAnimateRect( world, world^.change_rects[i] );
  665.         end;
  666.         world^.change_rect_count := 0;
  667.         
  668.     end;
  669.     
  670.     procedure NSSetPortToBackground( world: NSWorldPtr );
  671.     begin
  672.         SetGWorld( world^.background^.world, nil );
  673.     end;
  674.     
  675.     procedure NSSetPortToWork( world: NSWorldPtr );
  676.     begin
  677.         SetGWorld( world^.work^.world, nil );
  678.     end;
  679.     
  680.     procedure NSSetPortToSprite( world: NSWorldPtr );
  681.     begin
  682.         SetGWorld( world^.sprite^.world, nil );
  683.     end;
  684.     
  685.     procedure NSSetPortToWindow( world: NSWorldPtr );
  686.     begin
  687.         SetGWorld( world^.window^.world, nil );
  688.     end;
  689.     
  690.     function NSCreateSprite( var sprite: NSSpritePtr; world: NSWorldPtr; const source_rect, mask_rect: Rect ): OSStatus;
  691.         var
  692.             err: OSStatus;
  693.     begin
  694.         Assert( world <> nil );
  695.         Assert( (source_rect.left < source_rect.right) & (source_rect.top < source_rect.bottom) );
  696.         Assert( (world^.sprite^.bounds.left <= source_rect.left) & (source_rect.right <= world^.sprite^.bounds.right) );
  697.         Assert( (world^.sprite^.bounds.top <= source_rect.top) & (source_rect.bottom <= world^.sprite^.bounds.bottom) );
  698.         Assert( (world^.sprite^.bounds.left <= mask_rect.left) & (mask_rect.right <= world^.sprite^.bounds.right) );
  699.         Assert( (world^.sprite^.bounds.top<= mask_rect.top) & (mask_rect.bottom <= world^.sprite^.bounds.bottom) );
  700.         Assert( RectWidth( source_rect ) = RectWidth( mask_rect ) );
  701.         Assert( RectWidth( source_rect ) <= max_sprite_size );
  702.         Assert( RectHeight( source_rect ) = RectHeight( mask_rect ) );
  703.         Assert( RectHeight( source_rect ) <= max_sprite_size );
  704.         
  705.         err := MNewPtr( sprite, SizeOf(NSSprite) );
  706.         if err = noErr then begin
  707.             sprite^.visible := false;
  708.             sprite^.source_rect := source_rect;
  709.             sprite^.mask_rect := mask_rect;
  710.             sprite^.dest_rect := source_rect;
  711.             OffsetRectTo( sprite^.dest_rect, 0, 0 );
  712.             sprite^.blitter := NSCopyBitsNoMaskBlitter;
  713.             sprite^.world := world;
  714.             sprite^.refcon := 0;
  715.             sprite^.grid.next := nil;
  716.             sprite^.grid.prev := nil;
  717.             sprite^.grid.head := false;
  718.             
  719.             sprite^.next := world^.sprites;
  720.             world^.sprites := sprite;
  721.         end;
  722.         NSCreateSprite := err;
  723.     end;
  724.     
  725.     procedure NSDestroySprite( var sprite: NSSpritePtr );
  726.         var
  727.             lastsprite: NSSpritePtr;
  728.     begin
  729.         Assert( sprite <> nil );
  730.         Assert( sprite^.world <> nil );
  731.         Assert( sprite^.world^.sprites <> nil );
  732.         
  733.         RemoveSpriteFromGrid( sprite );
  734.         
  735.         if sprite^.world^.sprites = sprite then begin
  736.             sprite^.world^.sprites := sprite^.next;
  737.         end else begin
  738.             lastsprite := sprite^.world^.sprites;
  739.             while lastsprite^.next <> sprite do begin
  740.                 Assert( lastsprite^.next <> nil );
  741.                 lastsprite := lastsprite^.next;
  742.             end;
  743.             lastsprite^.next := sprite^.next;
  744.         end;
  745.         MDisposePtr( sprite );
  746.     end;
  747.     
  748.     procedure NSSetSpriteVisible( sprite: NSSpritePtr; visible: boolean );
  749.     begin
  750.         Assert( sprite <> nil );
  751.         Assert( sprite^.world <> nil );
  752.         
  753.         if sprite^.visible <> visible then begin
  754.             if not visible then begin
  755.                 RemoveSpriteFromGrid( sprite );
  756.             end;
  757.             NSAddChangeRect( sprite^.world, sprite^.dest_rect );
  758.             sprite^.visible := visible;
  759.             if visible then begin
  760.                 AddSpriteToGrid( sprite );
  761.             end;
  762.         end;
  763.     end;
  764.     
  765.     procedure NSSetSpriteLocation( sprite: NSSpritePtr; h, v: integer );
  766.     begin
  767.         Assert( sprite <> nil );
  768.         Assert( sprite^.world <> nil );
  769.         
  770.         if (h <> sprite^.dest_rect.left) | (v <> sprite^.dest_rect.top) then begin
  771.             if sprite^.visible then begin
  772.                 RemoveSpriteFromGrid( sprite );
  773.                 NSAddChangeRect( sprite^.world, sprite^.dest_rect );
  774.             end;
  775.             OffsetRect( sprite^.dest_rect, -sprite^.dest_rect.left + h, -sprite^.dest_rect.top + v );
  776.             if sprite^.visible then begin
  777.                 NSAddChangeRect( sprite^.world, sprite^.dest_rect );
  778.                 AddSpriteToGrid( sprite );
  779.             end;
  780.         end;
  781.     end;
  782.     
  783.     procedure NSOffsetSpriteLocation( sprite: NSSpritePtr; dh, dv: integer );
  784.     begin
  785.         Assert( sprite <> nil );
  786.         
  787.         NSSetSpriteLocation( sprite, sprite^.dest_rect.left + dh, sprite^.dest_rect.top + dv );
  788.     end;
  789.     
  790.     procedure NSSetSpriteBlitter( sprite: NSSpritePtr; blitter: BlitterProc );
  791.     begin
  792.         Assert( sprite <> nil );
  793.         
  794.         if blitter <> sprite^.blitter then begin
  795.             if sprite^.visible then begin
  796.                 NSAddChangeRect( sprite^.world, sprite^.dest_rect );
  797.             end;
  798.             sprite^.blitter := blitter;
  799.         end;
  800.     end;
  801.     
  802.     procedure NSSetSpriteSource( sprite: NSSpritePtr; const source_rect, mask_rect: Rect );
  803.     begin
  804.         Assert( sprite <> nil );
  805.         Assert( RectWidth( source_rect ) = RectWidth( mask_rect ) );
  806.         Assert( RectWidth( source_rect ) <= max_sprite_size );
  807.         Assert( RectHeight( source_rect ) = RectHeight( mask_rect ) );
  808.         Assert( RectHeight( source_rect ) <= max_sprite_size );
  809.         
  810.         if not EqualRect( source_rect, sprite^.source_rect ) | not EqualRect( mask_rect, sprite^.mask_rect ) then begin
  811.             if sprite^.visible then begin
  812.                 NSAddChangeRect( sprite^.world, sprite^.dest_rect );
  813.             end;
  814.             sprite^.source_rect := source_rect;
  815.             sprite^.mask_rect := mask_rect;
  816.             sprite^.dest_rect.right := sprite^.dest_rect.left + RectWidth( source_rect );
  817.             sprite^.dest_rect.bottom := sprite^.dest_rect.top + RectHeight( source_rect );
  818.             if sprite^.visible then begin
  819.                 NSAddChangeRect( sprite^.world, sprite^.dest_rect );
  820.             end;
  821.         end;
  822.     end;
  823.  
  824.     procedure NSSetSpriteRefcon( sprite: NSSpritePtr; refcon: longint );
  825.     begin
  826.         Assert( sprite <> nil );
  827.         
  828.         if refcon <> sprite^.refcon then begin
  829.             if sprite^.visible then begin
  830.                 NSAddChangeRect( sprite^.world, sprite^.dest_rect );
  831.             end;
  832.             sprite^.refcon := refcon;
  833.         end;
  834.     end;
  835.     
  836.     procedure NSInitSprites;
  837.     begin
  838. {$ifc not GENERATINGCFM}            
  839.         needSwapMMUMode := GetMMUMode <> true32b;
  840. {$endc}
  841.     end;
  842.  
  843. end.
  844.