home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / progrmng / stk110.lzh / STKSRC.COM / SPR.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-25  |  18.0 KB  |  552 lines

  1. /**********************************************************************
  2. * spr.c
  3. *
  4. * The sprite support system main functions
  5. **********************************************************************
  6.                     This file is part of
  7.  
  8.           STK -- The sprite toolkit -- version 1.1
  9.  
  10.               Copyright (C) Jari Karjala 1991
  11.  
  12. The sprite toolkit (STK) is a FreeWare toolkit for creating high
  13. resolution sprite graphics with PCompatible hardware. This toolkit 
  14. is provided as is without any warranty or such thing. See the file
  15. COPYING for further information.
  16.  
  17. **********************************************************************/
  18.  
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21.  
  22. #include <alloc.h>      /* farmalloc(), farfree() */
  23. #include <mem.h>        /* memcpy(), movedata() */
  24. #include <dos.h>        /* setvect(), getvect(), FP_SEG(), FP_OFS() */
  25. #include <graphics.h>   /* mode defines */
  26.  
  27. #include "sprP.h"
  28. #include "spr.h"
  29. #include "spr_err.h"
  30. #include "spr_low.h"
  31. #include "spr_misc.h"
  32. #include "spr_int8.h"
  33.  
  34. #include "gr.h"
  35. #include "gr_low.h"
  36.  
  37. /** display lists for each display page **/
  38. SPRITE spr_sprites[2] = { NULL, NULL };
  39.  
  40. /** the sprites to be destroyed after next_pass **/
  41. static SPRITE destroy_list = { NULL };
  42.  
  43. /** the current drawing page **/
  44. WORD spr_drawingpage = 0;
  45.  
  46. /** Non-zero as long as the spr_regulate_speed has to wait **/
  47. WORD spr_need_delay = 0;
  48.  
  49. /**********************************************************************
  50. * The delay after setvisualpage() before removing old objects. 
  51. * Time is given in milliseconds. Hercules cards do not need this,
  52. * but many EGA/VGA cards do need some milliseconds to switch pages.
  53. **********************************************************************/
  54. int spr_pass_delay = 10;
  55.  
  56.  
  57. /**********************************************************************
  58. * Called from low level funtions if not sprite system initialized.
  59. **********************************************************************/
  60. void static fatal_error()
  61. {
  62.     spr_err("Sprite system has not been initialized!");
  63. }
  64.  
  65. /** hardware dependent putter **/
  66. void static (*spr_low_putter)
  67.                     (WORD w, WORD h, 
  68.                      BYTE far *dest, BYTE far *shape, BYTE far *save 
  69.                      ) = fatal_error;
  70.                    
  71. /** hardware dependent eraser  **/
  72. void static (*spr_low_eraser)(WORD w, WORD h,
  73.                               BYTE far *dest, BYTE far *save
  74.                               ) = fatal_error;  
  75.  
  76. /** hardware dependent address calculator **/
  77. BYTE static far *(*spr_low_addr)(WORD x, WORD y, BYTE page) = fatal_error;
  78.  
  79.  
  80.  
  81. /**********************************************************************
  82. * Restore the original timer handler
  83. **********************************************************************/
  84. static void spr_restore_timer(void)
  85. {
  86.     setvect(8, spr_old_int8);    
  87. }
  88.  
  89. /**********************************************************************
  90. * Set a new handler for the timer.
  91. * This handler will reset the spr_need_delay flag to 0 and call
  92. * the old timer handler.
  93. **********************************************************************/
  94. static void spr_grab_timer(void)
  95. {
  96.     static int first_time = 1;
  97.     
  98.     spr_old_int8 = getvect(8);
  99.     setvect(8, spr_int8);
  100.     if (first_time) {
  101.         atexit(spr_restore_timer);
  102.         first_time = 0;
  103.     }
  104. }
  105.  
  106. /**********************************************************************
  107. * Initialize the sprite system to the given display hardware.
  108. * Supported graphicsdrivers: EGA (Only two colors), EGAMONO and HERCMONO.
  109. * The visual page is set to 0.
  110. * NOTE: This function must be called before any other sprite funtions
  111. *       and the graphics mode must have been set before this call.
  112. * graphicsdriver  The BGI identifier for the graphics driver used.
  113. **********************************************************************/
  114. void spr_initialize(int graphicsdriver)
  115. {
  116.     switch (graphicsdriver) {
  117.         case HERCMONO:
  118.             spr_low_putter = spr_low_herc_put;
  119.             spr_low_eraser = spr_low_herc_erase;
  120.             spr_low_addr   = gr_low_herc_addr;
  121.             spr_pass_delay = 1; /** hercules cards do not need this **/
  122.             break;
  123.             
  124.         case EGA:
  125.         case EGAMONO:
  126.             spr_low_putter = spr_low_ega_mono_put;
  127.             spr_low_eraser = spr_low_ega_mono_erase;
  128.             spr_low_addr   = gr_low_ega_mono_addr;
  129.             break;
  130.             
  131.         default:
  132.             spr_err("spr_initialize: unsupported screen mode");
  133.     }
  134.  
  135.     gr_setvisualpage(spr_drawingpage);
  136.     spr_grab_timer();
  137. }
  138.  
  139.  
  140.  
  141. /**********************************************************************
  142. * Create a sprite.
  143. *
  144. * w,h   Width (must be < 8*255) and height (<256) of sprite in pixels
  145. * pic   The picture bitmap for the sprite
  146. * mask  The mask bitmap for the sprite
  147. * res   The number of steps wanted per 8 bit interval in horizontal
  148. *       direction (1,2,4,8). For example, the value 8 gives one
  149. *       pixel resolution in X-direction.
  150. * ID    The user supplied ID for the sprite (not obligatory)
  151. *
  152. * Return: the newly created sprite or NULL if parameter error
  153. *           or out-of-memory
  154. **********************************************************************/
  155. SPRITE spr_create(WORD w, WORD h, 
  156.                   BITMAP shape, BITMAP mask, 
  157.                   BYTE res, WORD ID)
  158. {
  159.     SPRITE spr;
  160.     WORD size;
  161.     
  162.     if (w<8 || h<1 || w >= 8*255 || h > 255 || shape==NULL || mask==NULL)
  163.         return NULL;
  164.  
  165.     if (res!=1 && res!=2 && res!=4 && res!=8)
  166.         return NULL;
  167.     
  168.     spr = (SPRITE)malloc(sizeof(struct _sprite));
  169.     if (spr==NULL)
  170.         return NULL;
  171.  
  172.     if (w&7)
  173.         spr->w = (w>>3) + 1;    /* not exact byte boundary, round up */
  174.     else
  175.         spr->w = w>>3;
  176.  
  177.     if (res>1)      /** make space for right shifts **/
  178.         spr->w++;
  179.     
  180.     spr->wp = w;    
  181.     spr->hp = h;    
  182.     spr->res = res;
  183.     spr->flags = FLAG_NONE;
  184.     spr->id = ID;
  185.     spr->x = spr->y = (WORD)-1;     /* Place it out of screen initially */
  186.  
  187.     size = spr->w * spr->hp;         /* size of one bitmap */
  188.     spr->size = size * 2;           /* shape + mask size */
  189.     
  190.     /** malloc space for usage count, bitmaps + save buffer for old images **/
  191.     spr->data = (FARMAP)farmalloc(spr->size*(long)res + size*2L);
  192.     if (spr->data==NULL) {
  193.         free(spr);
  194.         return NULL;
  195.     }
  196.  
  197.     /** create shifted/combined shape/mask maps **/
  198.     spr_misc_create_data(spr, shape, mask);
  199.     
  200.     /** set save buffer address for both display pages **/
  201.     spr->saved[0] = spr->data + (spr->size * res);
  202.     spr->saved[1] = spr->data + (spr->size * res + size);
  203.     
  204.     spr->next[0] = spr->next[1] = NULL;
  205.     spr->addr[0] = spr->addr[1] = NULL;
  206.  
  207.     spr->share = NULL;
  208.     spr->share_index = 0;
  209.     spr->magic_number = SPR_MAGIC_NUMBER;
  210.     
  211.     return spr;
  212. }
  213.  
  214. /**********************************************************************
  215. * Create a shared version of the given sprite. This allows
  216. * n spr_copies which all share the shape data thus saving
  217. * quite much memory. We reallocate the save space for n sprites 
  218. * and we must have an array which tells us which parts of the save
  219. * space are occupied and which are free. All this hassle comes from
  220. * the fact that we have to keep save buffer in the same memory
  221. * block as the shape data (which is required to keep them both
  222. * in the same segment. This damned 64k segmenting architechture!)
  223. *
  224. * spr   The sprite to share
  225. * n     The maximum number of shared copies
  226. *
  227. * Return: New SPRITE or NULL if error (out of memory, spr==NULL, etc)
  228. **********************************************************************/
  229. SPRITE spr_share(SPRITE spr, BYTE n)
  230. {
  231.     WORD size, i;
  232.     
  233.     if (spr==NULL)
  234.         return NULL;
  235.  
  236.     if (spr->magic_number != SPR_MAGIC_NUMBER) {
  237.         spr_err("spr_share: invalid sprite handle");
  238.         return NULL;
  239.     }
  240.  
  241.     size = spr->w * spr->hp;        /* size of one bitmap */
  242.     n++;                            /* add one for the original sprite */
  243.     
  244.     /** Reallocate space for shapes, save space for copies and share index **/
  245.     spr->data = (FARMAP)farrealloc(spr->data,
  246.                                    spr->size*spr->res + n*size*2L + n);
  247.     
  248.     /** Reset old save buffer address for both display pages **/
  249.     /** (Realloc might move the block) **/
  250.     spr->saved[0] = spr->data + (spr->size * spr->res);
  251.     spr->saved[1] = spr->data + (spr->size * spr->res + size);
  252.     
  253.     /** Set share index pointer and reset its contents **/
  254.     spr->share = spr->data + (spr->size*spr->res + n*size*2L);
  255.     spr->share_index = 0;
  256.     spr->max_share = n-1;
  257.     spr->share[spr->share_index] = 1;
  258.     for (i=1; i<spr->max_share; i++)
  259.         spr->share[i] = 0;
  260.     
  261.     return spr;
  262. }
  263.  
  264.  
  265. /**********************************************************************
  266. * Return a copy of the given sprite.
  267. *
  268. * spr   The sprite to copy
  269. * id    The ID for the new sprite
  270. *
  271. * Return: New SPRITE of NULL if error (out of memory, spr==NULL, etc)
  272. **********************************************************************/
  273. SPRITE spr_copy(SPRITE spr_old, WORD id)
  274. {
  275.     SPRITE spr;
  276.     WORD size, i;
  277.  
  278.     if (spr_old==NULL)
  279.         return NULL;
  280.     
  281.     if (spr_old->magic_number != SPR_MAGIC_NUMBER) {
  282.         spr_err("spr_copy: invalid sprite handle");
  283.         return NULL;
  284.     }
  285.  
  286.     spr = (SPRITE)malloc(sizeof(struct _sprite));
  287.     if (spr==NULL)
  288.         return NULL;
  289.     memcpy(spr, spr_old, sizeof(struct _sprite));
  290.  
  291.     spr->id = id;
  292.     size = spr->w * spr->hp;         /* size of one bitmap */
  293.  
  294.     if (spr->share!=NULL) { /** we have a shared sprite **/
  295.         for (i=0; i<spr->max_share; i++)
  296.             if (spr->share[i]==0)
  297.                 break;
  298.         if (i==spr->max_share) {    /** no free slots **/
  299.             free(spr);
  300.             return NULL;
  301.         }
  302.         spr->saved[0] = spr->data + (spr->size * spr->res + (size*2)*i);
  303.         spr->saved[1] = spr->data + (spr->size * spr->res + (size*2)*i+size);
  304.         spr->share[i] = 1;
  305.         spr->share_index = i;
  306.     }
  307.     else {  /** not shared, must make a full copy **/
  308.         /** space for usage count, bitmaps + save buffer for old images **/
  309.         spr->data = (FARMAP)farmalloc(spr->size*spr->res + size*2L);
  310.         if (spr->data==NULL) {
  311.             free(spr);
  312.             return NULL;
  313.         }
  314.  
  315.         movedata(FP_SEG(spr_old->data), FP_OFF(spr_old->data),
  316.                  FP_SEG(spr->data), FP_OFF(spr->data),
  317.                  spr->size*spr->res);
  318.  
  319.         /** set save buffer address for both display pages **/
  320.         spr->saved[0] = spr->data + (spr->size * spr->res);
  321.         spr->saved[1] = spr->data + (spr->size * spr->res + size);
  322.     }
  323.  
  324.     return spr;
  325. }
  326.  
  327. /**********************************************************************
  328. * Put the sprite into the given position into the display list.
  329. * NOTE: the call has no effect on screen until a call to 
  330. *       spr_next_pass() is made.
  331. *
  332. * spr   The sprite to put
  333. * x     The X coordinate
  334. * y     The Y coordinate
  335. **********************************************************************/
  336. void spr_put(SPRITE spr, WORD x, WORD y)
  337. {
  338.     if (spr->magic_number != SPR_MAGIC_NUMBER) {
  339.         spr_err("spr_put: invalid sprite handle");
  340.         return;
  341.     }
  342.  
  343.     /** add sprite to the display list **/
  344.     spr->next[spr_drawingpage] = spr_sprites[spr_drawingpage];
  345.     spr_sprites[spr_drawingpage] = spr;
  346.     
  347.     /** save coordinates for collision checking & SHAPE_OFS macro **/
  348.     spr->x = x;
  349.     spr->y = y;
  350.     
  351.     spr->addr[spr_drawingpage] = spr_low_addr(x,y,spr_drawingpage);
  352. }
  353.  
  354. /**********************************************************************
  355. * Remove the sprite from the display list
  356. * NOTE: the call has no effect on screen until a call to 
  357. *       spr_next_pass() is made.
  358. *
  359. * spr   The sprite to hide
  360. **********************************************************************/
  361. void spr_hide(SPRITE spr)
  362. {
  363.     if (spr->magic_number != SPR_MAGIC_NUMBER) {
  364.         spr_err("spr_hide: invalid sprite handle");
  365.         return;
  366.     }
  367.  
  368.     spr_misc_delete(spr, spr_drawingpage);
  369. }
  370.  
  371. /**********************************************************************
  372. * Delete the given sprite and release associated memory buffers.
  373. * Also delete it from the display lists. (Actually the sprite is
  374. * only hidden and moved into the deletion list, the memory is freed
  375. * after the next_pass().)
  376. *
  377. * spr   The sprite to delete
  378. **********************************************************************/
  379. void spr_delete(SPRITE spr)
  380. {
  381.     if (spr->magic_number != SPR_MAGIC_NUMBER) {
  382.         spr_err("spr_delete: invalid sprite handle");
  383.         return;
  384.     }
  385.  
  386.     spr_hide(spr);
  387.     spr->next[spr_drawingpage] = destroy_list;
  388.     destroy_list = spr;    
  389. }
  390.  
  391. /**********************************************************************
  392. * Display all sprites in the chain in reversed order.
  393. * NOTE: this is recursive, so it needs 4-8 bytes (depending on memory
  394. * model) of stack space for each sprite.
  395. **********************************************************************/
  396. void static show_all_reversed(SPRITE s)
  397. {
  398.     if (s==NULL)
  399.         return;
  400.     
  401.     /** First descent recursively to the end of list **/
  402.     if (s->next[spr_drawingpage]!=NULL)
  403.         show_all_reversed(s->next[spr_drawingpage]);
  404.  
  405.     spr_low_putter(s->w, s->hp,
  406.                    s->addr[spr_drawingpage], s->data+SHAPE_OFS(s), 
  407.                    s->saved[spr_drawingpage]);
  408.                    
  409. }
  410.  
  411. /**********************************************************************
  412. * This function actually shows the sprites.
  413. * The following functions are performed (the order is important):
  414. * - put all sprite images into the hidden page (in reverse order).
  415. * - set hidden page to visual page (delay a moment for EGA cards).
  416. * - delete the old sprite images from the (now hidden) page.
  417. * - now it is safe to actually free the spr_deleted sprites.
  418. *
  419. * Return: The current visual page.
  420. **********************************************************************/
  421. WORD spr_next_pass(void)
  422. {
  423.     SPRITE s,s1;
  424.     int i;
  425.  
  426.     /** Display all sprites at once in reverse order **/
  427.     show_all_reversed(spr_sprites[spr_drawingpage]);
  428.     
  429.     gr_setvisualpage(spr_drawingpage); /** show 'em **/
  430.     delay(spr_pass_delay);          /** EGA/VGA cards are SLOW! **/    
  431.     
  432.     /** Then delete old ones **/
  433.     spr_drawingpage ^= 1;
  434.     s = spr_sprites[spr_drawingpage];
  435.     while (s!=NULL) {
  436.         spr_low_eraser(s->w,s->hp,
  437.                        s->addr[spr_drawingpage], s->saved[spr_drawingpage]);
  438.         s = s->next[spr_drawingpage];
  439.     }
  440.     spr_sprites[spr_drawingpage] = NULL;
  441.     
  442.     /** Destroy the sprites which have been spr_deleted **/
  443.     s = destroy_list;
  444.     while (s!=NULL) {
  445.         if (s->share!=NULL) { /** if shared must check for usage **/
  446.             s->share[s->share_index] = 0;
  447.             for (i=0; i<s->max_share; i++)
  448.                 if (s->share[i])
  449.                     break;
  450.             if (i==s->max_share)
  451.                 farfree(s->data);
  452.         }
  453.         else
  454.             farfree(s->data);
  455.         
  456.         s->magic_number = 0;    /** error if this sprite referenced later **/
  457.         s1 = s->next[spr_drawingpage^1];
  458.         free(s);
  459.         s = s1;
  460.     }
  461.     destroy_list = NULL;
  462.         
  463.     return spr_drawingpage^1;
  464. }
  465.  
  466. /**********************************************************************
  467. * This function tries to regulate the frame speed by delaying if
  468. * we are doing more frames per second than we should. The target 
  469. * speed is 1 frame per one clock tick (about 18 frames per second).
  470. * (This is about the highest reasonable rate for 15-20 32x24 pixel
  471. * sprites on a Hercules screen with a 10 MHz 286.)
  472. * The variable spr_need_delay is reset by the new timer handler.
  473. **********************************************************************/
  474. void spr_regulate_speed(void)
  475. {
  476.     while(spr_need_delay)
  477.         ;
  478.     spr_need_delay = 1;
  479.  
  480.     /**** This waits for vertical retrace with Hercules hardware => 50 fps
  481.     while (inportb(0x3BA) & 0x80)
  482.         ;
  483.      ****/
  484. }
  485.  
  486. /**********************************************************************
  487. * Return the user supplied identifier of the sprite
  488. **********************************************************************/
  489. WORD spr_get_id(SPRITE spr)
  490. {
  491.     if (spr->magic_number != SPR_MAGIC_NUMBER) {
  492.         spr_err("spr_get_id: invalid sprite handle");
  493.         return 0;
  494.     }
  495.  
  496.     return spr->id;
  497. }
  498.  
  499. /**********************************************************************
  500. * Return the X coordinate of the sprite
  501. **********************************************************************/
  502. WORD spr_get_x(SPRITE spr)
  503. {
  504.     if (spr->magic_number != SPR_MAGIC_NUMBER) {
  505.         spr_err("spr_get_x: invalid sprite handle");
  506.         return 0;
  507.     }
  508.  
  509.     return spr->x;
  510. }
  511.  
  512. /**********************************************************************
  513. * Return the Y coordinate of the sprite
  514. **********************************************************************/
  515. WORD spr_get_y(SPRITE spr)
  516. {
  517.     if (spr->magic_number != SPR_MAGIC_NUMBER) {
  518.         spr_err("spr_get_y: invalid sprite handle");
  519.         return 0;
  520.     }
  521.  
  522.     return spr->y;
  523. }
  524.  
  525. /**********************************************************************
  526. * Return the width of the sprite
  527. **********************************************************************/
  528. WORD spr_get_width(SPRITE spr)
  529. {
  530.     if (spr->magic_number != SPR_MAGIC_NUMBER) {
  531.         spr_err("spr_get_width: invalid sprite handle");
  532.         return 0;
  533.     }
  534.  
  535.     return spr->w*8;
  536. }
  537.  
  538. /**********************************************************************
  539. * Return the height of the sprite
  540. **********************************************************************/
  541. WORD spr_get_height(SPRITE spr)
  542. {
  543.     if (spr->magic_number != SPR_MAGIC_NUMBER) {
  544.         spr_err("spr_height: invalid sprite handle");
  545.         return 0;
  546.     }
  547.  
  548.     return spr->hp;
  549. }
  550.