home *** CD-ROM | disk | FTP | other *** search
/ Graphics Programming Black Book (Special Edition) / BlackBook.bin / disk1 / source / chapter46 / l46-1.c next >
C/C++ Source or Header  |  1997-06-18  |  15KB  |  368 lines

  1. /* Sample simple dirty-rectangle animation program, partially optimized and
  2. featuring internal animation, masked images (sprites), and nonoverlapping dirty
  3. rectangle copying.
  4. Tested with Borland C++ 4.02 in small model by Jim Mischel 12/16/94.
  5. */
  6. #include <stdlib.h>
  7. #include <conio.h>
  8. #include <alloc.h>
  9. #include <memory.h>
  10. #include <dos.h>
  11.  
  12. /* Comment out to disable overlap elimination in the dirty rectangle list. */
  13. #define CHECK_OVERLAP 1
  14. #define SCREEN_WIDTH  320
  15. #define SCREEN_HEIGHT 200
  16. #define SCREEN_SEGMENT 0xA000
  17.  
  18. /* Describes a dirty rectangle */
  19. typedef struct {
  20.    void *Next;    /* pointer to next node in linked dirty rect list */
  21.    int Top;
  22.    int Left;
  23.    int Right;
  24.    int Bottom;
  25. } DirtyRectangle;
  26. /* Describes an animated object */
  27. typedef struct {
  28.    int X;            /* upper left corner in virtual bitmap */
  29.    int Y;
  30.    int XDirection;   /* direction and distance of movement */
  31.    int YDirection;
  32.    int InternalAnimateCount; /* tracking internal animation state */
  33.    int InternalAnimateMax;   /* maximum internal animation state */
  34. } Entity;
  35. /* Storage used for dirty rectangles */
  36. #define MAX_DIRTY_RECTANGLES  100
  37. int NumDirtyRectangles;
  38. DirtyRectangle DirtyRectangles[MAX_DIRTY_RECTANGLES];
  39. /* Head/tail of dirty rectangle list */
  40. DirtyRectangle DirtyHead;
  41. /* If set to 1, ignore dirty rectangle list and copy the whole screen. */
  42. int DrawWholeScreen = 0;
  43. /* Pixels and masks for the two internally animated versions of the image
  44.    we'll animate */
  45. #define IMAGE_WIDTH  13
  46. #define IMAGE_HEIGHT 11
  47. char ImagePixels0[] = {
  48.    0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
  49.    0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0,
  50.    0, 9, 9, 0, 0,14,14,14, 9, 9, 0, 0, 0,
  51.    9, 9, 0, 0, 0, 0,14,14,14, 9, 9, 0, 0,
  52.    9, 9, 0, 0, 0, 0,14,14,14, 9, 9, 0, 0,
  53.    9, 9,14, 0, 0,14,14,14,14, 9, 9, 0, 0,
  54.    9, 9,14,14,14,14,14,14,14, 9, 9, 0, 0,
  55.    9, 9,14,14,14,14,14,14,14, 9, 9, 0, 0,
  56.    0, 9, 9,14,14,14,14,14, 9, 9, 0, 0, 0,
  57.    0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0,
  58.    0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
  59. };
  60. char ImageMask0[] = {
  61.    0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  62.    0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
  63.    0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  64.    1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
  65.    1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
  66.    1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
  67.    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  68.    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  69.    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
  70.    0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
  71.    0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  72. };
  73. char ImagePixels1[] = {
  74.    0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9,
  75.    0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9,
  76.    0, 9, 9, 0, 0,14,14,14, 9, 9, 9, 9, 0,
  77.    9, 9, 0, 0, 0, 0,14,14,14, 0, 0, 0, 0,
  78.    9, 9, 0, 0, 0, 0,14,14, 0, 0, 0, 0, 0,
  79.    9, 9,14, 0, 0,14,14,14, 0, 0, 0, 0, 0,
  80.    9, 9,14,14,14,14,14,14, 0, 0, 0, 0, 0,
  81.    9, 9,14,14,14,14,14,14,14, 0, 0, 0, 0,
  82.    0, 9, 9,14,14,14,14,14, 9, 9, 9, 9, 0,
  83.    0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9,
  84.    0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9,
  85. };
  86. char ImageMask1[] = {
  87.    0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
  88.    0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
  89.    0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
  90.    1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
  91.    1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
  92.    1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
  93.    1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  94.    1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
  95.    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  96.    0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
  97.    0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
  98. };
  99. /* Pointers to pixel and mask data for various internally animated
  100.    versions of our animated image. */
  101. char * ImagePixelArray[] = {ImagePixels0, ImagePixels1};
  102. char * ImageMaskArray[] = {ImageMask0, ImageMask1};
  103. /* Animated entities */
  104. #define NUM_ENTITIES 15
  105. Entity Entities[NUM_ENTITIES];
  106. /* Pointer to system buffer into which we'll draw */
  107. char far *SystemBufferPtr;
  108. /* Pointer to screen */
  109. char far *ScreenPtr;
  110. void EraseEntities(void);
  111. void CopyDirtyRectanglesToScreen(void);
  112. void DrawEntities(void);
  113. void AddDirtyRect(Entity *, int, int);
  114. void DrawMasked(char far *, char *, char *, int, int, int);
  115. void FillRect(char far *, int, int, int, int);
  116. void CopyRect(char far *, char far *, int, int, int, int);
  117. void main()
  118. {
  119.    int i, XTemp, YTemp;
  120.    unsigned int TempCount;
  121.    char far *TempPtr;
  122.    union REGS regs;
  123.    /* Allocate memory for the system buffer into which we'll draw */
  124.    if (!(SystemBufferPtr = farmalloc((unsigned int)SCREEN_WIDTH*
  125.          SCREEN_HEIGHT))) {
  126.       printf("Couldn't get memory\n");
  127.       exit(1);
  128.    }
  129.    /* Clear the system buffer */
  130.    TempPtr = SystemBufferPtr;
  131.    for (TempCount = ((unsigned)SCREEN_WIDTH*SCREEN_HEIGHT); TempCount--; ) {
  132.       *TempPtr++ = 0;
  133.    }
  134.    /* Point to the screen */
  135.    ScreenPtr = MK_FP(SCREEN_SEGMENT, 0);
  136.    /* Set up the entities we'll animate, at random locations */
  137.    randomize();
  138.    for (i = 0; i < NUM_ENTITIES; i++) {
  139.       Entities[i].X = random(SCREEN_WIDTH - IMAGE_WIDTH);
  140.       Entities[i].Y = random(SCREEN_HEIGHT - IMAGE_HEIGHT);
  141.       Entities[i].XDirection = 1;
  142.       Entities[i].YDirection = -1;
  143.       Entities[i].InternalAnimateCount = i & 1;
  144.       Entities[i].InternalAnimateMax = 2;
  145.    }
  146.    /* Set the dirty rectangle list to empty, and set up the head/tail node
  147.       as a sentinel */
  148.    NumDirtyRectangles = 0;
  149.    DirtyHead.Next = &DirtyHead;
  150.    DirtyHead.Top = 0x7FFF;
  151.    DirtyHead.Left= 0x7FFF;
  152.    DirtyHead.Bottom = 0x7FFF;
  153.    DirtyHead.Right = 0x7FFF;
  154.    /* Set 320x200 256-color graphics mode */
  155.    regs.x.ax = 0x0013;
  156.    int86(0x10, ®s, ®s);
  157.    /* Loop and draw until a key is pressed */
  158.    do {
  159.       /* Draw the entities to the system buffer at their current locations,
  160.          updating the dirty rectangle list */
  161.       DrawEntities();
  162.       /* Draw the dirty rectangles, or the whole system buffer if
  163.          appropriate */
  164.       CopyDirtyRectanglesToScreen();
  165.       /* Reset the dirty rectangle list to empty */
  166.       NumDirtyRectangles = 0;
  167.       DirtyHead.Next = &DirtyHead;
  168.       /* Erase the entities in the system buffer at their old locations,
  169.          updating the dirty rectangle list */
  170.       EraseEntities();
  171.       /* Move the entities, bouncing off the edges of the screen */
  172.       for (i = 0; i < NUM_ENTITIES; i++) {
  173.          XTemp = Entities[i].X + Entities[i].XDirection;
  174.          YTemp = Entities[i].Y + Entities[i].YDirection;
  175.          if ((XTemp < 0) || ((XTemp + IMAGE_WIDTH) > SCREEN_WIDTH)) {
  176.             Entities[i].XDirection = -Entities[i].XDirection;
  177.             XTemp = Entities[i].X + Entities[i].XDirection;
  178.          }
  179.          if ((YTemp < 0) || ((YTemp + IMAGE_HEIGHT) > SCREEN_HEIGHT)) {
  180.             Entities[i].YDirection = -Entities[i].YDirection;
  181.             YTemp = Entities[i].Y + Entities[i].YDirection;
  182.          }
  183.          Entities[i].X = XTemp;
  184.          Entities[i].Y = YTemp;
  185.       }
  186.    } while (!kbhit());
  187.    getch();    /* clear the keypress */
  188.  
  189.    /* Back to text mode */
  190.    regs.x.ax = 0x0003;
  191.    int86(0x10, ®s, ®s);
  192. }
  193. /* Draw entities at their current locations, updating dirty rectangle list. */
  194. void DrawEntities()
  195. {
  196.    int i;
  197.    char far *RowPtrBuffer;
  198.    char *TempPtrImage;
  199.    char *TempPtrMask;
  200.     Entity *EntityPtr;
  201.  
  202.    for (i = 0, EntityPtr = Entities; i < NUM_ENTITIES; i++, EntityPtr++) {
  203.       /* Remember the dirty rectangle info for this entity */
  204.       AddDirtyRect(EntityPtr, IMAGE_HEIGHT, IMAGE_WIDTH);
  205.       /* Point to the destination in the system buffer */
  206.       RowPtrBuffer = SystemBufferPtr + (EntityPtr->Y * SCREEN_WIDTH) +
  207.             EntityPtr->X;
  208.       /* Advance the image animation pointer */
  209.       if (++EntityPtr->InternalAnimateCount >=
  210.             EntityPtr->InternalAnimateMax) {
  211.          EntityPtr->InternalAnimateCount = 0;
  212.       }
  213.       /* Point to the image and mask to draw */
  214.       TempPtrImage = ImagePixelArray[EntityPtr->InternalAnimateCount];
  215.       TempPtrMask = ImageMaskArray[EntityPtr->InternalAnimateCount];
  216.       DrawMasked(RowPtrBuffer, TempPtrImage, TempPtrMask, IMAGE_HEIGHT,
  217.                IMAGE_WIDTH, SCREEN_WIDTH);
  218.    }
  219. }
  220. /* Copy the dirty rectangles, or the whole system buffer if appropriate,
  221.    to the screen. */
  222. void CopyDirtyRectanglesToScreen()
  223. {
  224.    int i, RectWidth, RectHeight;
  225.    unsigned int Offset;
  226.    DirtyRectangle * DirtyPtr;
  227.    if (DrawWholeScreen) {
  228.       /* Just copy the whole buffer to the screen */
  229.       DrawWholeScreen = 0;
  230.       CopyRect(ScreenPtr, SystemBufferPtr, SCREEN_HEIGHT, SCREEN_WIDTH,
  231.                SCREEN_WIDTH, SCREEN_WIDTH);
  232.    } else {
  233.       /* Copy only the dirty rectangles, in the YX-sorted order in which
  234.          they're linked */
  235.       DirtyPtr = DirtyHead.Next;
  236.       for (i = 0; i < NumDirtyRectangles; i++) {
  237.          /* Offset in both system buffer and screen of image */
  238.          Offset = (unsigned int) (DirtyPtr->Top * SCREEN_WIDTH) +
  239.                DirtyPtr->Left;
  240.          /* Dimensions of dirty rectangle */
  241.          RectWidth = DirtyPtr->Right - DirtyPtr->Left;
  242.          RectHeight = DirtyPtr->Bottom - DirtyPtr->Top;
  243.          /* Copy a dirty rectangle */
  244.          CopyRect(ScreenPtr + Offset, SystemBufferPtr + Offset,
  245.                RectHeight, RectWidth, SCREEN_WIDTH, SCREEN_WIDTH);
  246.          /* Point to the next dirty rectangle */
  247.          DirtyPtr = DirtyPtr->Next;
  248.       }
  249.    }
  250. }
  251. /* Erase the entities in the system buffer at their current locations,
  252.    updating the dirty rectangle list. */
  253. void EraseEntities()
  254. {
  255.    int i;
  256.    char far *RowPtr;
  257.    for (i = 0; i < NUM_ENTITIES; i++) {
  258.       /* Remember the dirty rectangle info for this entity */
  259.       AddDirtyRect(&Entities[i], IMAGE_HEIGHT, IMAGE_WIDTH);
  260.       /* Point to the destination in the system buffer */
  261.       RowPtr = SystemBufferPtr + (Entities[i].Y * SCREEN_WIDTH) +
  262.             Entities[i].X;
  263.       /* Clear the rectangle */
  264.       FillRect(RowPtr, IMAGE_HEIGHT, IMAGE_WIDTH, SCREEN_WIDTH, 0);
  265.    }
  266. }
  267. /* Add a dirty rectangle to the list. The list is maintained in top-to-bottom,
  268. left-to-right (YX sorted) order, with no pixel ever included twice, to minimize
  269. the number of display memory accesses and to avoid screen artifacts resulting 
  270. from a large time interval between erasure and redraw for a given object or for
  271. adjacent objects. The technique used is to check for overlap between the 
  272. rectangle and all rectangles already in the list. If no overlap is found, the 
  273. rectangle is added to the list. If overlap is found, the rectangle is broken 
  274. into nonoverlapping pieces, and the pieces are added to the list by recursive 
  275. calls to this function. */
  276. void AddDirtyRect(Entity * pEntity, int ImageHeight, int ImageWidth)
  277. {
  278.    DirtyRectangle * DirtyPtr;
  279.    DirtyRectangle * TempPtr;
  280.    Entity TempEntity;
  281.    int i;
  282.    if (NumDirtyRectangles >= MAX_DIRTY_RECTANGLES) {
  283.       /* Too many dirty rectangles; just redraw the whole screen */
  284.       DrawWholeScreen = 1;
  285.       return;
  286.    }
  287.    /* Remember this dirty rectangle. Break up if necessary to avoid
  288.       overlap with rectangles already in the list, then add whatever
  289.       rectangles are left, in YX sorted order */
  290. #ifdef CHECK_OVERLAP
  291.    /* Check for overlap with existing rectangles */
  292.    TempPtr = DirtyHead.Next;
  293.    for (i = 0; i < NumDirtyRectangles; i++, TempPtr = TempPtr->Next) {
  294.       if ((TempPtr->Left < (pEntity->X + ImageWidth)) &&
  295.           (TempPtr->Right > pEntity->X) &&
  296.           (TempPtr->Top < (pEntity->Y + ImageHeight)) &&
  297.           (TempPtr->Bottom > pEntity->Y)) {
  298.  
  299.          /* We've found an overlapping rectangle. Calculate the
  300.             rectangles, if any, remaining after subtracting out the
  301.             overlapped areas, and add them to the dirty list */
  302.          /* Check for a nonoverlapped left portion */
  303.          if (TempPtr->Left > pEntity->X) {
  304.             /* There's definitely a nonoverlapped portion at the left; add
  305.                it, but only to at most the top and bottom of the overlapping
  306.                rect; top and bottom strips are taken care of below */
  307.             TempEntity.X = pEntity->X;
  308.             TempEntity.Y = max(pEntity->Y, TempPtr->Top);
  309.             AddDirtyRect(&TempEntity,
  310.                   min(pEntity->Y + ImageHeight, TempPtr->Bottom) -
  311.                   TempEntity.Y,
  312.                         TempPtr->Left - pEntity->X);
  313.          }
  314.          /* Check for a nonoverlapped right portion */
  315.          if (TempPtr->Right < (pEntity->X + ImageWidth)) {
  316.             /* There's definitely a nonoverlapped portion at the right; add
  317.                it, but only to at most the top and bottom of the overlapping
  318.                rect; top and bottom strips are taken care of below */
  319.             TempEntity.X = TempPtr->Right;
  320.             TempEntity.Y = max(pEntity->Y, TempPtr->Top);
  321.             AddDirtyRect(&TempEntity,
  322.                   min(pEntity->Y + ImageHeight, TempPtr->Bottom) -
  323.                   TempEntity.Y,
  324.                   (pEntity->X + ImageWidth) - TempPtr->Right);
  325.          }
  326.          /* Check for a nonoverlapped top portion */
  327.          if (TempPtr->Top > pEntity->Y) {
  328.             /* There's a top portion that's not overlapped */
  329.             TempEntity.X = pEntity->X;
  330.             TempEntity.Y = pEntity->Y;
  331.             AddDirtyRect(&TempEntity, TempPtr->Top - pEntity->Y, ImageWidth);
  332.          }
  333.          /* Check for a nonoverlapped bottom portion */
  334.          if (TempPtr->Bottom < (pEntity->Y + ImageHeight)) {
  335.             /* There's a bottom portion that's not overlapped */
  336.             TempEntity.X = pEntity->X;
  337.             TempEntity.Y = TempPtr->Bottom;
  338.             AddDirtyRect(&TempEntity,
  339.                   (pEntity->Y + ImageHeight) - TempPtr->Bottom, ImageWidth);
  340.          }
  341.          /* We've added all non-overlapped portions to the dirty list */
  342.          return;
  343.       }
  344.    }
  345. #endif /* CHECK_OVERLAP */
  346.    /* There's no overlap with any existing rectangle, so we can just
  347.       add this rectangle as-is */
  348.    /* Find the YX-sorted insertion point. Searches will always terminate,
  349.       because the head/tail rectangle is set to the maximum values */
  350.    TempPtr = &DirtyHead;
  351.    while (((DirtyRectangle *)TempPtr->Next)->Top < pEntity->Y) {
  352.       TempPtr = TempPtr->Next;
  353.    }
  354.    while ((((DirtyRectangle *)TempPtr->Next)->Top == pEntity->Y) &&
  355.            (((DirtyRectangle *)TempPtr->Next)->Left < pEntity->X)) {
  356.       TempPtr = TempPtr->Next;
  357.    }
  358.    /* Set the rectangle and actually add it to the dirty list */
  359.    DirtyPtr = &DirtyRectangles[NumDirtyRectangles++];
  360.    DirtyPtr->Left = pEntity->X;
  361.    DirtyPtr->Top = pEntity->Y;
  362.    DirtyPtr->Right = pEntity->X + ImageWidth;
  363.    DirtyPtr->Bottom = pEntity->Y + ImageHeight;
  364.    DirtyPtr->Next = TempPtr->Next;
  365.    TempPtr->Next = DirtyPtr;
  366. }
  367.  
  368.