home *** CD-ROM | disk | FTP | other *** search
/ Shareware Supreme Volume 6 #1 / swsii.zip / swsii / 215 / DDJ9302.ZIP / GRPHPRO.ASC < prev    next >
Text File  |  1993-01-11  |  21KB  |  519 lines

  1. _GRAPHICS PROGRAMMING COLUMN_
  2. by Michael Abrash
  3.  
  4. [LISTING ONE]
  5.  
  6. /* Sample simple dirty-rectangle animation program, partially optimized and
  7. featuring internal animation, masked images (sprites), and nonoverlapping dirty
  8. rectangle copying. Tested with Borland C++ 3.0 in the small model. */
  9.  
  10. #include <stdlib.h>
  11. #include <conio.h>
  12. #include <alloc.h>
  13. #include <memory.h>
  14. #include <dos.h>
  15.  
  16. /* Comment out to disable overlap elimination in the dirty rectangle list. */
  17. #define CHECK_OVERLAP 1
  18. #define SCREEN_WIDTH  320
  19. #define SCREEN_HEIGHT 200
  20. #define SCREEN_SEGMENT 0xA000
  21.  
  22. /* Describes a dirty rectangle */
  23. typedef struct {
  24.    void *Next;    /* pointer to next node in linked dirty rect list */
  25.    int Top;
  26.    int Left;
  27.    int Right;
  28.    int Bottom;
  29. } DirtyRectangle;
  30. /* Describes an animated object */
  31. typedef struct {
  32.    int X;            /* upper left corner in virtual bitmap */
  33.    int Y;
  34.    int XDirection;   /* direction and distance of movement */
  35.    int YDirection;
  36.    int InternalAnimateCount; /* tracking internal animation state */
  37.    int InternalAnimateMax;   /* maximum internal animation state */
  38. } Entity;
  39. /* Storage used for dirty rectangles */
  40. #define MAX_DIRTY_RECTANGLES  100
  41. int NumDirtyRectangles;
  42. DirtyRectangle DirtyRectangles[MAX_DIRTY_RECTANGLES];
  43. /* Head/tail of dirty rectangle list */
  44. DirtyRectangle DirtyHead;
  45. /* If set to 1, ignore dirty rectangle list and copy the whole screen. */
  46. int DrawWholeScreen = 0;
  47. /* Pixels and masks for the two internally animated versions of the image
  48.    we'll animate */
  49. #define IMAGE_WIDTH  13
  50. #define IMAGE_HEIGHT 11
  51. char ImagePixels0[] = {
  52.    0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
  53.    0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0,
  54.    0, 9, 9, 0, 0,14,14,14, 9, 9, 0, 0, 0,
  55.    9, 9, 0, 0, 0, 0,14,14,14, 9, 9, 0, 0,
  56.    9, 9, 0, 0, 0, 0,14,14,14, 9, 9, 0, 0,
  57.    9, 9,14, 0, 0,14,14,14,14, 9, 9, 0, 0,
  58.    9, 9,14,14,14,14,14,14,14, 9, 9, 0, 0,
  59.    9, 9,14,14,14,14,14,14,14, 9, 9, 0, 0,
  60.    0, 9, 9,14,14,14,14,14, 9, 9, 0, 0, 0,
  61.    0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0,
  62.    0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
  63. };
  64. char ImageMask0[] = {
  65.    0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  66.    0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
  67.    0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  68.    1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
  69.    1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
  70.    1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
  71.    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  72.    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  73.    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
  74.    0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
  75.    0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  76. };
  77. char ImagePixels1[] = {
  78.    0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9,
  79.    0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9,
  80.    0, 9, 9, 0, 0,14,14,14, 9, 9, 9, 9, 0,
  81.    9, 9, 0, 0, 0, 0,14,14,14, 0, 0, 0, 0,
  82.    9, 9, 0, 0, 0, 0,14,14, 0, 0, 0, 0, 0,
  83.    9, 9,14, 0, 0,14,14,14, 0, 0, 0, 0, 0,
  84.    9, 9,14,14,14,14,14,14, 0, 0, 0, 0, 0,
  85.    9, 9,14,14,14,14,14,14,14, 0, 0, 0, 0,
  86.    0, 9, 9,14,14,14,14,14, 9, 9, 9, 9, 0,
  87.    0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9,
  88.    0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9,
  89. };
  90. char ImageMask1[] = {
  91.    0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
  92.    0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
  93.    0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
  94.    1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
  95.    1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
  96.    1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
  97.    1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  98.    1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
  99.    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  100.    0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
  101.    0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
  102. };
  103. /* Pointers to pixel and mask data for various internally animated
  104.    versions of our animated image. */
  105. char * ImagePixelArray[] = {ImagePixels0, ImagePixels1};
  106. char * ImageMaskArray[] = {ImageMask0, ImageMask1};
  107. /* Animated entities */
  108. #define NUM_ENTITIES 15
  109. Entity Entities[NUM_ENTITIES];
  110. /* Pointer to system buffer into which we'll draw */
  111. char far *SystemBufferPtr;
  112. /* Pointer to screen */
  113. char far *ScreenPtr;
  114. void EraseEntities(void);
  115. void CopyDirtyRectanglesToScreen(void);
  116. void DrawEntities(void);
  117. void AddDirtyRect(Entity *, int, int);
  118. void DrawMasked(char far *, char *, char *, int, int, int);
  119. void FillRect(char far *, int, int, int, int);
  120. void CopyRect(char far *, char far *, int, int, int, int);
  121. void main()
  122. {
  123.    int i, XTemp, YTemp;
  124.    unsigned int TempCount;
  125.    char far *TempPtr;
  126.    union REGS regs;
  127.    /* Allocate memory for the system buffer into which we'll draw */
  128.    if (!(SystemBufferPtr = farmalloc((unsigned int)SCREEN_WIDTH*
  129.          SCREEN_HEIGHT))) {
  130.       printf("Couldn't get memory\n");
  131.       exit(1);
  132.    }
  133.    /* Clear the system buffer */
  134.    TempPtr = SystemBufferPtr;
  135.    for (TempCount = ((unsigned)SCREEN_WIDTH*SCREEN_HEIGHT); TempCount--; ) {
  136.       *TempPtr++ = 0;
  137.    }
  138.    /* Point to the screen */
  139.    ScreenPtr = MK_FP(SCREEN_SEGMENT, 0);
  140.    /* Set up the entities we'll animate, at random locations */
  141.    randomize();
  142.    for (i = 0; i < NUM_ENTITIES; i++) {
  143.       Entities[i].X = random(SCREEN_WIDTH - IMAGE_WIDTH);
  144.       Entities[i].Y = random(SCREEN_HEIGHT - IMAGE_HEIGHT);
  145.       Entities[i].XDirection = 1;
  146.       Entities[i].YDirection = -1;
  147.       Entities[i].InternalAnimateCount = i & 1;
  148.       Entities[i].InternalAnimateMax = 2;
  149.    }
  150.    /* Set the dirty rectangle list to empty, and set up the head/tail node
  151.       as a sentinel */
  152.    NumDirtyRectangles = 0;
  153.    DirtyHead.Next = &DirtyHead;
  154.    DirtyHead.Top = 0x7FFF;
  155.    DirtyHead.Left= 0x7FFF;
  156.    DirtyHead.Bottom = 0x7FFF;
  157.    DirtyHead.Right = 0x7FFF;
  158.    /* Set 320x200 256-color graphics mode */
  159.    regs.x.ax = 0x0013;
  160.    int86(0x10, ®s, ®s);
  161.    /* Loop and draw until a key is pressed */
  162.    do {
  163.       /* Draw the entities to the system buffer at their current locations,
  164.          updating the dirty rectangle list */
  165.       DrawEntities();
  166.       /* Draw the dirty rectangles, or the whole system buffer if
  167.          appropriate */
  168.       CopyDirtyRectanglesToScreen();
  169.       /* Reset the dirty rectangle list to empty */
  170.       NumDirtyRectangles = 0;
  171.       DirtyHead.Next = &DirtyHead;
  172.       /* Erase the entities in the system buffer at their old locations,
  173.          updating the dirty rectangle list */
  174.       EraseEntities();
  175.       /* Move the entities, bouncing off the edges of the screen */
  176.       for (i = 0; i < NUM_ENTITIES; i++) {
  177.          XTemp = Entities[i].X + Entities[i].XDirection;
  178.          YTemp = Entities[i].Y + Entities[i].YDirection;
  179.          if ((XTemp < 0) || ((XTemp + IMAGE_WIDTH) > SCREEN_WIDTH)) {
  180.             Entities[i].XDirection = -Entities[i].XDirection;
  181.             XTemp = Entities[i].X + Entities[i].XDirection;
  182.          }
  183.          if ((YTemp < 0) || ((YTemp + IMAGE_HEIGHT) > SCREEN_HEIGHT)) {
  184.             Entities[i].YDirection = -Entities[i].YDirection;
  185.             YTemp = Entities[i].Y + Entities[i].YDirection;
  186.          }
  187.          Entities[i].X = XTemp;
  188.          Entities[i].Y = YTemp;
  189.       }
  190.    } while (!kbhit());
  191.    getch();    /* clear the keypress */
  192.  
  193.    /* Back to text mode */
  194.    regs.x.ax = 0x0003;
  195.    int86(0x10, ®s, ®s);
  196. }
  197. /* Draw entities at their current locations, updating dirty rectangle list. */
  198. void DrawEntities()
  199. {
  200.    int i;
  201.    char far *RowPtrBuffer;
  202.    char *TempPtrImage;
  203.    char *TempPtrMask;
  204.     Entity *EntityPtr;
  205.  
  206.    for (i = 0, EntityPtr = Entities; i < NUM_ENTITIES; i++, EntityPtr++) {
  207.       /* Remember the dirty rectangle info for this entity */
  208.       AddDirtyRect(EntityPtr, IMAGE_HEIGHT, IMAGE_WIDTH);
  209.       /* Point to the destination in the system buffer */
  210.       RowPtrBuffer = SystemBufferPtr + (EntityPtr->Y * SCREEN_WIDTH) +
  211.             EntityPtr->X;
  212.       /* Advance the image animation pointer */
  213.       if (++EntityPtr->InternalAnimateCount >=
  214.             EntityPtr->InternalAnimateMax) {
  215.          EntityPtr->InternalAnimateCount = 0;
  216.       }
  217.       /* Point to the image and mask to draw */
  218.       TempPtrImage = ImagePixelArray[EntityPtr->InternalAnimateCount];
  219.       TempPtrMask = ImageMaskArray[EntityPtr->InternalAnimateCount];
  220.       DrawMasked(RowPtrBuffer, TempPtrImage, TempPtrMask, IMAGE_HEIGHT,
  221.                IMAGE_WIDTH, SCREEN_WIDTH);
  222.    }
  223. }
  224. /* Copy the dirty rectangles, or the whole system buffer if appropriate,
  225.    to the screen. */
  226. void CopyDirtyRectanglesToScreen()
  227. {
  228.    int i, RectWidth, RectHeight;
  229.    unsigned int Offset;
  230.    DirtyRectangle * DirtyPtr;
  231.    if (DrawWholeScreen) {
  232.       /* Just copy the whole buffer to the screen */
  233.       DrawWholeScreen = 0;
  234.       CopyRect(ScreenPtr, SystemBufferPtr, SCREEN_HEIGHT, SCREEN_WIDTH,
  235.                SCREEN_WIDTH, SCREEN_WIDTH);
  236.    } else {
  237.       /* Copy only the dirty rectangles, in the YX-sorted order in which
  238.          they're linked */
  239.       DirtyPtr = DirtyHead.Next;
  240.       for (i = 0; i < NumDirtyRectangles; i++) {
  241.          /* Offset in both system buffer and screen of image */
  242.          Offset = (unsigned int) (DirtyPtr->Top * SCREEN_WIDTH) +
  243.                DirtyPtr->Left;
  244.          /* Dimensions of dirty rectangle */
  245.          RectWidth = DirtyPtr->Right - DirtyPtr->Left;
  246.          RectHeight = DirtyPtr->Bottom - DirtyPtr->Top;
  247.          /* Copy a dirty rectangle */
  248.          CopyRect(ScreenPtr + Offset, SystemBufferPtr + Offset,
  249.                RectHeight, RectWidth, SCREEN_WIDTH, SCREEN_WIDTH);
  250.          /* Point to the next dirty rectangle */
  251.          DirtyPtr = DirtyPtr->Next;
  252.       }
  253.    }
  254. }
  255. /* Erase the entities in the system buffer at their current locations,
  256.    updating the dirty rectangle list. */
  257. void EraseEntities()
  258. {
  259.    int i;
  260.    char far *RowPtr;
  261.    for (i = 0; i < NUM_ENTITIES; i++) {
  262.       /* Remember the dirty rectangle info for this entity */
  263.       AddDirtyRect(&Entities[i], IMAGE_HEIGHT, IMAGE_WIDTH);
  264.       /* Point to the destination in the system buffer */
  265.       RowPtr = SystemBufferPtr + (Entities[i].Y * SCREEN_WIDTH) +
  266.             Entities[i].X;
  267.       /* Clear the rectangle */
  268.       FillRect(RowPtr, IMAGE_HEIGHT, IMAGE_WIDTH, SCREEN_WIDTH, 0);
  269.    }
  270. }
  271. /* Add a dirty rectangle to the list. The list is maintained in top-to-bottom,
  272. left-to-right (YX sorted) order, with no pixel ever included twice, to minimize
  273. the number of display memory accesses and to avoid screen artifacts resulting 
  274. from a large time interval between erasure and redraw for a given object or for
  275. adjacent objects. The technique used is to check for overlap between the 
  276. rectangle and all rectangles already in the list. If no overlap is found, the 
  277. rectangle is added to the list. If overlap is found, the rectangle is broken 
  278. into nonoverlapping pieces, and the pieces are added to the list by recursive 
  279. calls to this function. */
  280. void AddDirtyRect(Entity * pEntity, int ImageHeight, int ImageWidth)
  281. {
  282.    DirtyRectangle * DirtyPtr;
  283.    DirtyRectangle * TempPtr;
  284.    Entity TempEntity;
  285.    int i;
  286.    if (NumDirtyRectangles >= MAX_DIRTY_RECTANGLES) {
  287.       /* Too many dirty rectangles; just redraw the whole screen */
  288.       DrawWholeScreen = 1;
  289.       return;
  290.    }
  291.    /* Remember this dirty rectangle. Break up if necessary to avoid
  292.       overlap with rectangles already in the list, then add whatever
  293.       rectangles are left, in YX sorted order */
  294. #ifdef CHECK_OVERLAP
  295.    /* Check for overlap with existing rectangles */
  296.    TempPtr = DirtyHead.Next;
  297.    for (i = 0; i < NumDirtyRectangles; i++, TempPtr = TempPtr->Next) {
  298.       if ((TempPtr->Left < (pEntity->X + ImageWidth)) &&
  299.           (TempPtr->Right > pEntity->X) &&
  300.           (TempPtr->Top < (pEntity->Y + ImageHeight)) &&
  301.           (TempPtr->Bottom > pEntity->Y)) {
  302.  
  303.          /* We've found an overlapping rectangle. Calculate the
  304.             rectangles, if any, remaining after subtracting out the
  305.             overlapped areas, and add them to the dirty list */
  306.          /* Check for a nonoverlapped left portion */
  307.          if (TempPtr->Left > pEntity->X) {
  308.             /* There's definitely a nonoverlapped portion at the left; add
  309.                it, but only to at most the top and bottom of the overlapping
  310.                rect; top and bottom strips are taken care of below */
  311.             TempEntity.X = pEntity->X;
  312.             TempEntity.Y = max(pEntity->Y, TempPtr->Top);
  313.             AddDirtyRect(&TempEntity,
  314.                   min(pEntity->Y + ImageHeight, TempPtr->Bottom) -
  315.                   TempEntity.Y,
  316.                         TempPtr->Left - pEntity->X);
  317.          }
  318.          /* Check for a nonoverlapped right portion */
  319.          if (TempPtr->Right < (pEntity->X + ImageWidth)) {
  320.             /* There's definitely a nonoverlapped portion at the right; add
  321.                it, but only to at most the top and bottom of the overlapping
  322.                rect; top and bottom strips are taken care of below */
  323.             TempEntity.X = TempPtr->Right;
  324.             TempEntity.Y = max(pEntity->Y, TempPtr->Top);
  325.             AddDirtyRect(&TempEntity,
  326.                   min(pEntity->Y + ImageHeight, TempPtr->Bottom) -
  327.                   TempEntity.Y,
  328.                   (pEntity->X + ImageWidth) - TempPtr->Right);
  329.          }
  330.          /* Check for a nonoverlapped top portion */
  331.          if (TempPtr->Top > pEntity->Y) {
  332.             /* There's a top portion that's not overlapped */
  333.             TempEntity.X = pEntity->X;
  334.             TempEntity.Y = pEntity->Y;
  335.             AddDirtyRect(&TempEntity, TempPtr->Top - pEntity->Y, ImageWidth);
  336.          }
  337.          /* Check for a nonoverlapped bottom portion */
  338.          if (TempPtr->Bottom < (pEntity->Y + ImageHeight)) {
  339.             /* There's a bottom portion that's not overlapped */
  340.             TempEntity.X = pEntity->X;
  341.             TempEntity.Y = TempPtr->Bottom;
  342.             AddDirtyRect(&TempEntity,
  343.                   (pEntity->Y + ImageHeight) - TempPtr->Bottom, ImageWidth);
  344.          }
  345.          /* We've added all non-overlapped portions to the dirty list */
  346.          return;
  347.       }
  348.    }
  349. #endif /* CHECK_OVERLAP */
  350.    /* There's no overlap with any existing rectangle, so we can just
  351.       add this rectangle as-is */
  352.    /* Find the YX-sorted insertion point. Searches will always terminate,
  353.       because the head/tail rectangle is set to the maximum values */
  354.    TempPtr = &DirtyHead;
  355.    while (((DirtyRectangle *)TempPtr->Next)->Top < pEntity->Y) {
  356.       TempPtr = TempPtr->Next;
  357.    }
  358.    while ((((DirtyRectangle *)TempPtr->Next)->Top == pEntity->Y) &&
  359.            (((DirtyRectangle *)TempPtr->Next)->Left < pEntity->X)) {
  360.       TempPtr = TempPtr->Next;
  361.    }
  362.    /* Set the rectangle and actually add it to the dirty list */
  363.    DirtyPtr = &DirtyRectangles[NumDirtyRectangles++];
  364.    DirtyPtr->Left = pEntity->X;
  365.    DirtyPtr->Top = pEntity->Y;
  366.    DirtyPtr->Right = pEntity->X + ImageWidth;
  367.    DirtyPtr->Bottom = pEntity->Y + ImageHeight;
  368.    DirtyPtr->Next = TempPtr->Next;
  369.    TempPtr->Next = DirtyPtr;
  370. }
  371.  
  372.  
  373. [LISTING TWO]
  374.  
  375. ; Assembly language helper routines for dirty rectangle animation. Tested with
  376. ; TASM 3.0. Fills a rectangle in the specified buffer. C-callable as:  
  377. ;  void FillRect(char far * BufferPtr, int RectHeight, int RectWidth,
  378. ;                   int BufferWidth, int Color);
  379.         .model  small
  380.         .code
  381. parms   struc
  382.                 dw      ?       ;pushed BP
  383.                 dw      ?       ;pushed return address
  384. BufferPtr       dd      ?       ;far pointer to buffer in which to fill
  385. RectHeight      dw      ?       ;height of rectangle to fill
  386. RectWidth       dw      ?       ;width of rectangle to fill
  387. BufferWidth     dw      ?       ;width of buffer in which to fill
  388. Color           dw      ?       ;color with which to fill
  389. parms   ends
  390.         public  _FillRect
  391. _FillRect   proc  near
  392.         cld
  393.         push    bp
  394.         mov     bp,sp
  395.         push    di
  396.  
  397.         les     di,[bp+BufferPtr]
  398.         mov     dx,[bp+RectHeight]
  399.         mov     bx,[bp+BufferWidth]
  400.         sub     bx,[bp+RectWidth]       ;distance from end of one dest scan
  401.                                         ; to start of next
  402.         mov     al,byte ptr [bp+Color]
  403.         mov     ah,al                   ;double the color for REP STOSW
  404. RowLoop:
  405.         mov     cx,[bp+RectWidth]
  406.         shr     cx,1
  407.         rep     stosw
  408.         adc     cx,cx
  409.         rep     stosb
  410.         add     di,bx                   ;point to next scan to fill
  411.         dec     dx                      ;count down rows to fill
  412.         jnz     RowLoop
  413.  
  414.         pop     di
  415.         pop     bp
  416.         ret
  417. _FillRect   endp
  418.  
  419. ; Draws a masked image (a sprite) to the specified buffer. C-callable as:
  420. ;     void DrawMasked(char far * BufferPtr, char * Pixels, char * Mask,
  421. ;                   int ImageHeight, int ImageWidth, int BufferWidth);
  422. parms2  struc
  423.                 dw      ?       ;pushed BP
  424.                 dw      ?       ;pushed return address
  425. BufferPtr2      dd      ?       ;far pointer to buffer in which to draw
  426. Pixels          dw      ?       ;pointer to image pixels
  427. Mask            dw      ?       ;pointer to image mask
  428. ImageHeight     dw      ?       ;height of image to draw
  429. ImageWidth      dw      ?       ;width of image to draw
  430. BufferWidth2    dw      ?       ;width of buffer in which to draw
  431. parms2  ends
  432.         public  _DrawMasked
  433. _DrawMasked     proc    near
  434.         cld
  435.         push    bp
  436.         mov     bp,sp
  437.         push    si
  438.         push    di
  439.  
  440.         les     di,[bp+BufferPtr2]
  441.         mov     si,[bp+Mask]
  442.         mov     bx,[bp+Pixels]
  443.         mov     dx,[bp+ImageHeight]
  444.         mov     ax,[bp+BufferWidth2]
  445.         sub     ax,[bp+ImageWidth]      ;distance from end of one dest scan
  446.         mov     [bp+BufferWidth2],ax    ; to start of next
  447. RowLoop2:
  448.         mov     cx,[bp+ImageWidth]
  449. ColumnLoop:
  450.         lodsb                           ;get the next mask byte
  451.         and     al,al                   ;draw this pixel?
  452.         jz      SkipPixel               ;no
  453.         mov     al,[bx]                 ;yes, draw the pixel
  454.         mov     es:[di],al
  455. SkipPixel:
  456.         inc     bx                      ;point to next source pixel
  457.         inc     di                      ;point to next dest pixel
  458.         dec     cx
  459.         jnz     ColumnLoop
  460.         add     di,[bp+BufferWidth2]    ;point to next scan to fill
  461.         dec     dx                      ;count down rows to fill
  462.         jnz     RowLoop2
  463.  
  464.         pop     di
  465.         pop     si
  466.         pop     bp
  467.         ret
  468. _DrawMasked     endp
  469.  
  470. ; Copies a rectangle from one buffer to another. C-callable as:
  471. ;     void CopyRect(DestBufferPtr, SrcBufferPtr, CopyHeight, CopyWidth,
  472. ;                   DestBufferWidth, SrcBufferWidth);
  473.  
  474. parms3  struc
  475.                 dw      ?       ;pushed BP
  476.                 dw      ?       ;pushed return address
  477. DestBufferPtr   dd      ?       ;far pointer to buffer to which to copy
  478. SrcBufferPtr    dd      ?       ;far pointer to buffer from which to copy
  479. CopyHeight      dw      ?       ;height of rect to copy
  480. CopyWidth       dw      ?       ;width of rect to copy
  481. DestBufferWidth dw      ?       ;width of buffer to which to copy
  482. SrcBufferWidth  dw      ?       ;width of buffer from which to copy
  483. parms3  ends
  484.         public  _CopyRect
  485. _CopyRect       proc    near
  486.         cld
  487.         push    bp
  488.         mov     bp,sp
  489.         push    si
  490.         push    di
  491.         push    ds
  492.  
  493.         les     di,[bp+DestBufferPtr]
  494.         lds     si,[bp+SrcBufferPtr]
  495.         mov     dx,[bp+CopyHeight]
  496.         mov     bx,[bp+DestBufferWidth] ;distance from end of one dest scan
  497.         sub     bx,[bp+CopyWidth]       ; of copy to the next
  498.         mov     ax,[bp+SrcBufferWidth]  ;distance from end of one source scan
  499.         sub     ax,[bp+CopyWidth]       ; of copy to the next
  500. RowLoop3:
  501.         mov     cx,[bp+CopyWidth]       ;# of bytes to copy
  502.         shr     cx,1
  503.         rep     movsw                   ;copy as many words as possible
  504.         adc     cx,cx
  505.         rep     movsb                   ;copy odd byte, if any
  506.         add     si,ax                   ;point to next source scan line
  507.         add     di,bx                   ;point to next dest scan line
  508.         dec     dx                      ;count down rows to fill
  509.         jnz     RowLoop3
  510.  
  511.         pop     ds
  512.         pop     di
  513.         pop     si
  514.         pop     bp
  515.         ret
  516. _CopyRect       endp
  517.         end
  518.  
  519.