home *** CD-ROM | disk | FTP | other *** search
/ Multimedia Jumpstart / Multimedia Microsoft Jumpstart Version 1.1a (Microsoft).BIN / develpmt / source / sprites / draw.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-03  |  16.9 KB  |  711 lines

  1. /*
  2.     draw.c
  3.  
  4.     Drawing routines
  5.  
  6.     If SHOWCLIP is defined then it will draw rectangles around regions 
  7.     drawn on the off-screen DC in one color and regions draw to the main
  8.     window in another color to show what's going on.
  9.  
  10. */
  11.  
  12. #include "global.h"
  13.  
  14. #define CYAN RGB(0,255,255)
  15. #define MAGENTA RGB(255,0,255)
  16.  
  17. //
  18. // local data
  19. //
  20.  
  21. static DRAWRECTLIST DrawList;
  22.  
  23. //
  24. // local functions
  25. //
  26.  
  27. static void DrawRect(HDC hDC, int x1, int y1, int x2, int y2, COLORREF col);
  28.  
  29.  
  30.  
  31.  
  32. //
  33. // Render a DIB to the off-screen DC
  34. // Note that because the off-screen DC belongs to the DIB driver
  35. // which is *not* a palettized device, we do not select/realize
  36. // any palette.  If this routine were altered to render the DIB to
  37. // an arbitrary DC then code would be needed to select/realize the
  38. // palette before calling StretchDIBits and unselect it after the call.
  39. // You must use DIB_RGB_COLORS here as the DIB driver (being a 
  40. // non-paletized device) can't handle DIB_PAL_COLORS and GDI will try
  41. // to be helpful and map them to some random RGB values.
  42. //
  43. // This routine was replaced by RenderDIBBitsOffScreen but is left
  44. // here as an example of how StretchDIbits could be used
  45. //
  46.  
  47. void RenderDIBOffScreen(PDIB pDIB, int iX, int iY, LPRECT prcClip)
  48. {
  49.     DWORD dwTicks;
  50.     int xs, ys, xd, yd, w, h;
  51.  
  52.     DBGSET(0x08);
  53.  
  54.     dprintf4("RenderDIB()");
  55.     dwTicks = timeGetTime();
  56.  
  57.     if (!hdcOffScreen) return;
  58.  
  59.     w = DIB_WIDTH(pDIB);
  60.     xd = iX;
  61.     xs = 0;
  62.     if (prcClip) {
  63.         if (prcClip->left > iX) {
  64.             xd += prcClip->left - iX;
  65.             xs += prcClip->left - iX;
  66.             w -= prcClip->left - iX;
  67.         }
  68.         if (xd+w > prcClip->right) {
  69.             w -= xd+w - prcClip->right;    
  70.         }
  71.     }
  72.  
  73.     h = DIB_HEIGHT(pDIB);
  74.     yd = iY;
  75.     ys = 0;
  76.     if (prcClip) {
  77.         if (prcClip->top > iY) {
  78.             yd += prcClip->top - iY;
  79.             h -= prcClip->top - iY;
  80.         }
  81.         if (iY+h > prcClip->bottom) {
  82.             ys = iY+h - prcClip->bottom;
  83.             h -= iY+h - prcClip->bottom;
  84.         }
  85.     }
  86.  
  87.     ASSERT(DIB_PBITS(pDIB));
  88.  
  89.     StretchDIBits(hdcOffScreen,
  90.                 xd,                     // dest x
  91.                 yd,                     // dest y
  92.                 w,                      // dest width
  93.                 h,                      // dest height
  94.                 xs,                     // src x
  95.                 ys,                     // src y
  96.                 w,                      // src width
  97.                 h,                      // src height
  98.                 DIB_PBITS(pDIB),        // bits
  99.                 DIB_PBI(pDIB),          // BITMAPINFO
  100.                 DIB_RGB_COLORS,
  101.                 SRCCOPY);               // rop
  102.  
  103.     if (bShowUpdateRects && prcClip) {
  104.         DrawRect(hdcOffScreen, xd+1, yd+1, xd+w-2, yd+h-2, CYAN);
  105.     }
  106.  
  107.     dprintf4("DIB render took %lu ms", timeGetTime() - dwTicks);
  108.  
  109.     DBGCLEAR(0x08);
  110. }
  111.  
  112. //
  113. // Render a DIB to the off-screen DC by just copying its bits.
  114. // Note that the DIB and the off-screen DIb must be the same format
  115. // (we only do 8 bpp) and *must* be using the same color table since
  116. // no mapping will be done
  117. //
  118.  
  119. void RenderDIBBitsOffScreen(PDIB pDIB, int x, int y, LPRECT prcClip,
  120.                             BYTE bTranClr, BOOL bTrans)
  121. {
  122.     RECT rcDraw, rcDIB;
  123.     HPBYTE pStartS;
  124.     HPBYTE pStartD;
  125.     long lScanS, lScanD;
  126.  
  127.     DBGSET(0x08);
  128.  
  129.     //
  130.     // Intersect the clip rect with the off-screen DIB to make
  131.     // sure we don't try to draw to any invalid coords
  132.     //
  133.  
  134.     rcDraw.top = rcDraw.left = 0;
  135.     rcDraw.right = DIB_WIDTH(pdibOffScreen) - 1;
  136.     rcDraw.bottom = DIB_HEIGHT(pdibOffScreen) - 1;
  137.  
  138.     if (prcClip) {
  139.         if (!IntersectRect(&rcDraw, &rcDraw, prcClip)) return;
  140.     }
  141.  
  142.     //
  143.     // Intersect the clip rect with the DIB rect
  144.     //
  145.  
  146.     rcDIB.left = x;
  147.     rcDIB.right = x + DIB_WIDTH(pDIB) - 1;
  148.     rcDIB.top = y;
  149.     rcDIB.bottom = y + DIB_HEIGHT(pDIB) - 1;
  150.  
  151.     if (!IntersectRect(&rcDraw, &rcDraw, &rcDIB)) return;
  152.  
  153.     //
  154.     // We have a sprite which is at least partially visible in
  155.     // the clip rect
  156.     // Calculate the address of the first pixel of the bottom 
  157.     // scan line
  158.     //
  159.  
  160.     pStartS = GetDIBPixelAddress(pDIB,
  161.                                  rcDraw.left - x,
  162.                                  rcDraw.bottom - y);
  163.     ASSERT(pStartS);
  164.  
  165.     //
  166.     // Calculate the address of the off-screen DIB
  167.     //
  168.  
  169.     pStartD = GetDIBPixelAddress(pdibOffScreen,
  170.                                  rcDraw.left,
  171.                                  rcDraw.bottom);
  172.  
  173.  
  174.     //
  175.     // Calculate the scan line width of the DIBs
  176.     //
  177.  
  178.     lScanS = DIB_STORAGEWIDTH(pDIB);
  179.     lScanD = DIB_STORAGEWIDTH(pdibOffScreen);
  180.  
  181.     if (!bTrans) {
  182.  
  183.         //
  184.         // Copy the bits without transparency
  185.         //
  186.  
  187.         CopyDIBBits(pStartD, 
  188.                     pStartS, 
  189.                     rcDraw.right - rcDraw.left + 1,
  190.                     rcDraw.bottom - rcDraw.top + 1,
  191.                     lScanD,
  192.                     lScanS);
  193.  
  194.     } else {
  195.  
  196.         TransCopyDIBBits(pStartD, 
  197.                          pStartS, 
  198.                          rcDraw.right - rcDraw.left + 1,
  199.                          rcDraw.bottom - rcDraw.top + 1,
  200.                          lScanD,
  201.                          lScanS,
  202.                          bTranClr);
  203.  
  204.     }
  205.  
  206.  
  207.     if (bShowUpdateRects) {
  208.         DrawRect(hdcOffScreen, 
  209.                  rcDraw.left+1,
  210.                  rcDraw.top+1,
  211.                  rcDraw.right-2,
  212.                  rcDraw.bottom-2,
  213.                  CYAN);
  214.     }
  215.  
  216.     DBGCLEAR(0x08);
  217. }
  218.  
  219. //
  220. // Render a sprite to a DC
  221. //
  222.  
  223. void RenderSpriteOffScreen(PSPRITE pSprite, LPRECT prcClip)
  224. {
  225.     RECT rc;
  226.  
  227.     if (!hdcOffScreen) return;
  228.  
  229.     DBGSET(0x04);
  230.     dprintf4("RenderSprite()");
  231.  
  232.     //
  233.     // See if the sprite rect is visible in the clip rect
  234.     //
  235.  
  236.     if (prcClip) {
  237.         GetSpriteRect(pSprite, &rc);
  238.         if (!IntersectRect(&rc, &rc, prcClip)) {
  239.             return;
  240.         }
  241.     }
  242.  
  243. #ifdef USESTRETCHDIBITS // old slow way
  244.  
  245.     SetBkColor(hdcOffScreen, pSprite->rgbTopLeft);
  246.     SetBkMode(hdcOffScreen, NEWTRANSPARENT);
  247.  
  248.     RenderDIBOffScreen(pSprite->pDIB,
  249.                        pSprite->x,
  250.                        pSprite->y,
  251.                        prcClip);
  252.               
  253.     SetBkMode(hdcOffScreen, OPAQUE);
  254.  
  255. #else // doing our own bit copy - faster
  256.  
  257.     RenderDIBBitsOffScreen(pSprite->pDIB,
  258.                            pSprite->x,
  259.                            pSprite->y,
  260.                            prcClip,
  261.                            pSprite->bTopLeft,
  262.                            TRUE); // transparent
  263.  
  264. #endif
  265.  
  266.     DBGCLEAR(0x04);
  267. }
  268.  
  269. //
  270. // Process WM_PAINT messages for the main window
  271. // We simply copy the clip rectangle from the off-screen DC
  272. // to the window DC by calling StretchDIBits.  We use the
  273. // DIB_PAL_COLORS option to avoid a color match and use the
  274. // 1:1 color table so that the index values stay the same.
  275. //
  276.  
  277. void Paint(HDC hDC, LPRECT prcClip)
  278. {
  279.     int w, h, xs, ys, xd, yd;
  280.     HPALETTE hOldPal;
  281.     DWORD dwStart, dwReal;
  282.  
  283.     DBGSET(0x10);
  284.     if (prcClip) {
  285.         dprintf2("Paint(%d,%d,%d,%d)",
  286.                  prcClip->left,
  287.                  prcClip->top,
  288.                  prcClip->right,
  289.                  prcClip->bottom);
  290.     } else {
  291.         dprintf2("Paint(NULL)");
  292.     }
  293.  
  294.     if (!hdcOffScreen) {
  295.         dprintf1("No off-screen DC to paint from");
  296.         return;
  297.     }
  298.  
  299.     dwStart = timeGetTime();
  300.  
  301.     //
  302.     // Copy the update rectangle from the off-screen DC
  303.     // to the window DC. Note that DIB origin is lower left
  304.     // corner.
  305.     //
  306.  
  307.     if (prcClip) {
  308.         w = prcClip->right - prcClip->left;
  309.         h = prcClip->bottom - prcClip->top;
  310.         xs = xd = prcClip->left;
  311.         yd = prcClip->top;
  312.         ys = DIB_HEIGHT(pdibOffScreen) - prcClip->bottom;
  313.     } else {
  314.  
  315.         w = DIB_WIDTH(pdibOffScreen);
  316.         h = DIB_HEIGHT(pdibOffScreen);
  317.         xs = xd = ys = yd = 0;
  318.     }
  319.  
  320.  
  321.     if (hpalCurrent) {
  322.         hOldPal = SelectPalette(hDC, hpalCurrent, 0);
  323.         RealizePalette(hDC);
  324.     }
  325.     dwReal = timeGetTime();
  326.     dprintf4(" Palette sel/real took %lu ms.", dwReal - dwStart);
  327.  
  328.     ASSERT(pPalClrTable);
  329.     StretchDIBits(hDC,              // dest dc
  330.                 xd,                 // dest x
  331.                 yd,                 // dest y
  332.                 w,                  // dest width
  333.                 h,                  // dest height
  334.                 xs,                 // src x
  335.                 ys,                 // src y
  336.                 w,                  // src width
  337.                 h,                  // src height
  338.                 DIB_PBITS(pdibOffScreen), // bits
  339.                 pPalClrTable,       // BITMAPINFO
  340.                 DIB_PAL_COLORS,     // options
  341.                 SRCCOPY);           // rop
  342.  
  343.     dprintf4(" StretchDIBits took %lu ms", timeGetTime() - dwReal);
  344.  
  345.     if (bShowUpdateRects) {
  346.         DrawRect(hDC, xd, yd, xd+w, yd+h, MAGENTA);
  347.     }
  348.  
  349.     if (hOldPal) SelectPalette(hDC, hOldPal, 0);
  350.  
  351.     DBGCLEAR(0x10);
  352. }
  353.  
  354. //
  355. // Draw a rectangle border of a given color
  356. //
  357.  
  358. static void DrawRect(HDC hDC, int x1, int y1, int x2, int y2, COLORREF col)
  359. {
  360.     HPEN hpenCol, hpenOld;
  361.     HBRUSH hbrOld;
  362.  
  363.     dprintf3("DrawRect(%d,%d,%d,%d)", x1, y1, x2, y2);
  364.  
  365.     hpenCol = CreatePen(PS_SOLID, 2, col);
  366.     hpenOld = SelectObject(hDC, hpenCol);
  367.     hbrOld = SelectObject(hDC, GetStockObject(NULL_BRUSH));
  368.     Rectangle(hDC, x1, y1, x2, y2);
  369.     SelectObject(hDC, hpenOld);
  370.     SelectObject(hDC, hbrOld);
  371.     DeleteObject(hpenCol);
  372. }
  373.  
  374. //
  375. // Update sprite positions (just for silly effect)
  376. //
  377.  
  378. void UpdatePositions()
  379. {
  380.     static DWORD dwLast, dwNow;
  381.  
  382.     PSPRITE pSprite;
  383.     BOOL bChanged = FALSE;
  384.     RECT rcPos;
  385.  
  386.     DBGSET(0x01);
  387.  
  388.     if (dwLast) {
  389.         dwNow = timeGetTime();
  390.         dprintf4("Update interval: %lu ms", dwNow-dwLast);
  391.         dwLast = dwNow;
  392.     } else {
  393.         dwLast = timeGetTime();
  394.     }
  395.  
  396.     pSprite = pSpriteList;
  397.     while (pSprite) {
  398.  
  399.         if (pSprite->vx || pSprite->vy) {
  400.  
  401.             GetSpriteRect(pSprite, &rcPos);
  402.             Redraw(&rcPos, NO_UPDATE);
  403.  
  404.             pSprite->x += pSprite->vx;
  405.             if ((pSprite->vx < 0) && (pSprite->x + pSprite->width < 0)) {
  406.                 pSprite->x = DIB_WIDTH(pdibBkGnd);
  407.             } else if ((pSprite->vx > 0) && (pSprite->x > DIB_WIDTH(pdibBkGnd))) {
  408.                 pSprite->x = - (int)pSprite->width;
  409.             }
  410.  
  411.             pSprite->y += pSprite->vy;
  412.             if ((pSprite->vy < 0) && (pSprite->y + pSprite->height < 0)) {
  413.                 pSprite->y = DIB_HEIGHT(pdibBkGnd);
  414.             } else if ((pSprite->vy > 0) && (pSprite->y > DIB_HEIGHT(pdibBkGnd))) {
  415.                 pSprite->y = - (int) pSprite->height;
  416.             }
  417.  
  418.             GetSpriteRect(pSprite, &rcPos);
  419.             Redraw(&rcPos, NO_UPDATE);
  420.  
  421.             bChanged = TRUE;
  422.         }
  423.  
  424.         pSprite = pSprite->pNext;
  425.     }
  426.  
  427.     if (bChanged) {
  428.         Redraw(&rcPos, UPDATE_SCREEN); // ask for the last one again
  429.     }
  430.     DBGCLEAR(0x01);
  431. }
  432.  
  433. //
  434. // Fill the off screen DIB with a set pattern (for palette testing)
  435. //
  436.  
  437. void FillBkGnd()
  438. {
  439.     int x, y, i, j, w, h;
  440.     BYTE color;
  441.     HPSTR hpBits;
  442.  
  443.     dprintf2("FillBkGnd()");
  444.  
  445.     if (!pdibOffScreen) {
  446.         dprintf1("No os DIB");
  447.         return;
  448.     }
  449.  
  450.     w = DIB_WIDTH(pdibOffScreen);
  451.     h = DIB_HEIGHT(pdibOffScreen);
  452.  
  453.     dprintf3(" %u x %u. Addr: %8.8lXH", w, h, DIB_PBITS(pdibOffScreen));
  454.  
  455.     ASSERT(w);
  456.     ASSERT(h);
  457.  
  458.     hpBits = (HPSTR) DIB_PBITS(pdibOffScreen);
  459.     for (y=0; y<h; y++) {
  460.         j = 15 - (y * 16 / h);
  461.  
  462.         for (x=0; x<w; x++) {
  463.             i = x * 16 / w;
  464.  
  465.             color = (BYTE) (j * 16 + i);
  466.  
  467.             *hpBits++ = color;
  468.  
  469.         }
  470.     }
  471.  
  472.     //
  473.     // Force a redraw
  474.     //
  475.  
  476.     InvalidateRect(hwndMain, NULL, FALSE);
  477.  
  478. }
  479.  
  480. //
  481. // Render the background and sprite set and repaint the window
  482. // if required.
  483. // If the NO_UPDATE option is requested, the clip rectangle
  484. // is added to the redraw list.  When an update is requested,
  485. // the redraw list is merged and all the rectangles in it
  486. // redrawn.  The list is reset to empty.
  487. //
  488.  
  489. void Redraw(LPRECT prcClip, BOOL bUpdate)
  490. {
  491.     PSPRITE pSprite, pLastSprite;
  492.     HDC hDC;
  493.     PDRAWRECT pDrawRect;
  494.     RECT rcAll;
  495.  
  496.     DBGSET(0x02);
  497.  
  498.     //
  499.     // add the rectangle to the list
  500.     //
  501.  
  502.     if (prcClip) {
  503.         AddDrawRectItem(&DrawList, prcClip);
  504.     } else {
  505.  
  506.         //
  507.         // if we have a background defined,
  508.         // add a rectangle to cover it
  509.         //
  510.  
  511.         if (pdibBkGnd) {
  512.             rcAll.left = rcAll.top = 0;
  513.             rcAll.right = DIB_WIDTH(pdibBkGnd);
  514.             rcAll.bottom = DIB_HEIGHT(pdibBkGnd);
  515.             AddDrawRectItem(&DrawList, &rcAll);
  516.         }
  517.  
  518.     }
  519.  
  520.     //
  521.     // If no update is requested, that's all there is
  522.     //
  523.  
  524.     if (bUpdate == NO_UPDATE) return;
  525.  
  526.     //
  527.     // make sure there is a background dib and dc
  528.     //
  529.  
  530.     if (!pdibBkGnd || !hdcOffScreen) return;
  531.  
  532.     //
  533.     // Merge the draw rect list and walk it doing redraws
  534.     //
  535.  
  536.     MergeDrawRectList(&DrawList);
  537.  
  538.     //
  539.     // Find the end of the sprite list
  540.     //
  541.  
  542.     pLastSprite = pSpriteList;
  543.     if (pLastSprite) {
  544.         while (pLastSprite->pNext) pLastSprite = pLastSprite->pNext;
  545.     }
  546.  
  547.     if (bUpdate == UPDATE_SCREEN) {
  548.         hDC = GetDC(hwndMain);
  549.     }
  550.  
  551.     //
  552.     // walk the draw rect list
  553.     //
  554.  
  555.     pDrawRect = DrawList.pHead;
  556.  
  557.     while (pDrawRect) {
  558.  
  559.         //
  560.         // Render the background DIB to the off-screen DC
  561.         //
  562.     
  563. #ifdef USESTRETCHDIBITS // old way
  564.  
  565.         RenderDIBOffScreen(pdibBkGnd, 0, 0, &(pDrawRect->rc));
  566.  
  567. #else // do our own faster thing
  568.  
  569.         RenderDIBBitsOffScreen(pdibBkGnd, 
  570.                                0, 0, 
  571.                                &(pDrawRect->rc),
  572.                                0,
  573.                                FALSE);
  574. #endif
  575.     
  576.         //
  577.         // Draw the sprites
  578.         // Walk the list from the bottom (back) to the top (front)
  579.         //
  580.  
  581.         pSprite = pLastSprite;
  582.         while (pSprite) {
  583.             RenderSpriteOffScreen(pSprite, &(pDrawRect->rc));
  584.             pSprite = pSprite->pPrev;
  585.         }
  586.     
  587.         //
  588.         // see if we need to repaint
  589.         //
  590.     
  591.         if (bUpdate == UPDATE_SCREEN) {
  592.     
  593.             Paint(hDC, &(pDrawRect->rc));
  594.         }
  595.  
  596.  
  597.         pDrawRect = pDrawRect->pNext;
  598.     }
  599.  
  600.     if (bUpdate == UPDATE_SCREEN) {
  601.         ReleaseDC(hwndMain, hDC);
  602.     }
  603.  
  604.     //
  605.     // empty the redraw list
  606.     //
  607.  
  608.     EmptyDrawRectList(&DrawList);
  609.  
  610.     DBGCLEAR(0x02);
  611. }
  612.  
  613. //
  614. // Delete a draw rect item from a list
  615. //
  616.  
  617. void DeleteDrawRectListItem(PDRAWRECTLIST pList, PDRAWRECT pItem)
  618. {
  619.     PDRAWRECT pPrev, pNext;
  620.  
  621.     pPrev = pItem->pPrev;
  622.     pNext = pItem->pNext;
  623.  
  624.     if (pNext) {
  625.         pNext->pPrev = pPrev;
  626.     }
  627.  
  628.     if (pPrev) {
  629.         pPrev->pNext = pNext;
  630.     } else {
  631.         pList->pHead = pNext;
  632.     }
  633.  
  634.     LocalFree((HANDLE)(pItem));
  635. }
  636.  
  637. //
  638. // Empty an entire draw rect list
  639. //
  640.  
  641. void EmptyDrawRectList(PDRAWRECTLIST pList)
  642. {
  643.     while (pList->pHead) DeleteDrawRectListItem(pList, pList->pHead);
  644. }
  645.  
  646. //
  647. // Add a rect to the top of the list
  648. //
  649.  
  650. void AddDrawRectItem(PDRAWRECTLIST pList, LPRECT pRect)
  651. {
  652.     PDRAWRECT pItem;
  653.  
  654.     DBGSET(0x20);
  655.     pItem = (PDRAWRECT) LocalAlloc(LPTR, sizeof(DRAWRECT));
  656.     if (!pItem) {
  657.         dprintf1("No memory for draw rect");
  658.     } else {
  659.         pItem->rc = *pRect;
  660.         pItem->pNext = pList->pHead;
  661.         pItem->pPrev = NULL;
  662.         pList->pHead = pItem;
  663.         if (pItem->pNext) {
  664.             pItem->pNext->pPrev = pItem;
  665.         }
  666.     }
  667.     DBGCLEAR(0x20);
  668. }
  669.  
  670. //
  671. // Merge together any overlapping rectangles in a list
  672. // This isn't such a great piece of code.  If you're a link-list
  673. // kind of guy - feel free to redo this and send me email
  674. // telling me how cool it is now:  nigelt@microsoft.com :-)
  675. //
  676.  
  677. void MergeDrawRectList(PDRAWRECTLIST pList)
  678. {
  679.     PDRAWRECT pItem1, pItem2, pNext;
  680.     BOOL bChanged;
  681.     RECT rcNew;
  682.  
  683.     if (!pList || !pList->pHead) return;
  684.  
  685.     DBGSET(0x40);
  686.     do {
  687.         bChanged = FALSE;
  688.  
  689.         pItem1 = pList->pHead;
  690.         while (pItem1) {
  691.  
  692.             pItem2 = pItem1->pNext;
  693.             while (pItem2) {
  694.  
  695.                 if (IntersectRect(&rcNew, &(pItem1->rc), &(pItem2->rc))) {
  696.  
  697.                     UnionRect(&(pItem1->rc), &(pItem1->rc), &(pItem2->rc));
  698.                     pNext = pItem2->pNext;
  699.                     DeleteDrawRectListItem(pList, pItem2);
  700.                     bChanged = TRUE;
  701.                     break;
  702.                 }
  703.                 pItem2 = pItem2->pNext;
  704.             }
  705.             if (bChanged) break;
  706.             pItem1 = pItem1->pNext;
  707.         }
  708.     } while (bChanged);
  709.     DBGCLEAR(0x40);
  710. }
  711.