Code Search for Developers
 
 
  

exlights.c from Allegro game programming library at Krugle


Show exlights.c syntax highlighted

/*
 *    Example program for the Allegro library, by Shawn Hargreaves.
 *
 *    This program shows one way to implement colored lighting effects
 *    in a hicolor video mode. Warning: it is not for the faint of heart!
 *    This is by no means the simplest or easiest to understand method,
 *    I just thought it was a cool concept that would be worth 
 *    demonstrating.
 *
 *    The basic approach is to select a 15 or 16 bit al_screen mode, but
 *    then draw onto 24 bit memory bitmaps. Since we only need the bottom
 *    5 bits of each 8 bit color in order to store 15 bit data within a
 *    24 bit location, we can fit a light level into the top 3 bits.
 *    The tricky bit is that these aren't actually 24 bit images at all:
 *    they are implemented as 8 bit memory bitmaps, and we just store the
 *    red level in one pixel, green in the next, and blue in the next,
 *    making the total image be three times wider than we really wanted.
 *    This allows us to use all the normal 256 color graphics routines
 *    for drawing onto our memory surfaces, most importantly the lookup
 *    table translucency, which can be used to combine the low 5 bits
 *    of color and the top 3 bits of light in a single drawing operation.
 *    Some trickery is needed to load 24 bit data into this fake 8 bit
 *    format, and of course it needs a custom routine to convert the
 *    resulting image while copying it across to the hardware al_screen.
 *
 *    This program chugs slightly on my p133, but not significantly 
 *    worse than any double buffering in what amounts to a 1920x640, 
 *    256 color resolution. The light blending doesn't seem to slow 
 *    it down too badly, so I think this technique would be quite usable 
 *    on faster machines and in lower resolution hicolor modes. The 
 *    biggest problem is that although you keep the full 15 bit color 
 *    resolution, you only get 3 bits of light, ie. 8 light levels. 
 *    You can do some nice colored light patches, but smooth gradients 
 *    aren't going to work too well :-)
 */


#include "allegro.h"



/* double buffer bitmap */
AL_BITMAP *buffer;


/* the two moving bitmap images */
AL_BITMAP *image1;
AL_BITMAP *image2;


/* the light map, drawn wherever the mouse pointer is */
AL_BITMAP *lightmap;


/* give images some light of their own. Make this zero for total black */
#define AMBIENT_LIGHT      0x20



/* converts a bitmap from the normal Allegro format into our magic layout */
AL_BITMAP *get_magic_bitmap_format(AL_BITMAP *orig, AL_PALETTE pal)
{
   AL_BITMAP *bmp;
   int c, r, g, b;
   int x, y; 
   int bpp; 

   /* create an 8 bpp image, three times as wide as the original */
   bmp = al_create_bitmap_ex(8, orig->w*3, orig->h);

   /* store info about the original bitmap format */
   bpp = al_bitmap_color_depth(orig);
   al_select_palette(pal);

   /* convert the data */
   for (y=0; y<orig->h; y++) {
      for (x=0; x<orig->w; x++) {
	 c = al_get_pixel(orig, x, y);

	 r = al_get_r_depth(bpp, c);
	 g = al_get_g_depth(bpp, c);
	 b = al_get_b_depth(bpp, c);

	 bmp->al_draw_line[y][x*3] = r*31/255 | AMBIENT_LIGHT;
	 bmp->al_draw_line[y][x*3+1] = g*31/255 | AMBIENT_LIGHT;
	 bmp->al_draw_line[y][x*3+2] = b*31/255 | AMBIENT_LIGHT;
      }
   }

   /* return our new, magic format version of the image */
   return bmp;
}



/* creates the light graphic for the mouse pointer, in our magic format */
AL_BITMAP *create_light_graphic(void)
{
   AL_BITMAP *bmp;
   int x, y;
   int dx, dy;
   int dist, dir;
   int r, g, b;

   bmp = al_create_bitmap_ex(8, 256*3, 256);

   /* draw the light (a circular color gradient) */
   for (y=0; y<256; y++) {
      for (x=0; x<256; x++) {
	 dx = x-128;
	 dy = y-128;

	 dist = al_fix_to_int(al_fix_sqrt(al_int_to_fix(MID(-32768, dx*dx+dy*dy, 32767))));

	 dir = al_fix_to_int(al_fix_atan2(al_int_to_fix(dy), al_int_to_fix(dx)));

	 al_hsv_to_rgb(dir*360.0/256.0, MID(0, dist/128.0, 1), 1, &r, &g, &b);

	 r = r * (128-dist) / 1024;
	 g = g * (128-dist) / 1024;
	 b = b * (128-dist) / 1024;

	 bmp->al_draw_line[y][x*3] = MID(0, r, 7) << 5;
	 bmp->al_draw_line[y][x*3+1] = MID(0, g, 7) << 5;
	 bmp->al_draw_line[y][x*3+2] = MID(0, b, 7) << 5;
      }
   }

   /* draw some solid pixels as well (a cross in the middle ) */
   #define magic_putpix(x, y, r, g, b)       \
   {                                         \
      bmp->al_draw_line[y][(x)*3] &= 0xE0;           \
      bmp->al_draw_line[y][(x)*3+1] &= 0xE0;         \
      bmp->al_draw_line[y][(x)*3+2] &= 0xE0;         \
					     \
      bmp->al_draw_line[y][(x)*3] |= r;              \
      bmp->al_draw_line[y][(x)*3+1] |= g;            \
      bmp->al_draw_line[y][(x)*3+2] |= b;            \
   }

   for (x=-15; x<=15; x++) {
      for (y=-2; y<=2; y++) {
	 magic_putpix(128+x, 128+y, x+15, 15-x, 0);
	 magic_putpix(128+y, 128+x, x+15, x+15, 15-x);
      }
   }

   return bmp;
}



/* lookup tables for speeding up the color conversion */
unsigned short rgtable[65536];
unsigned long brtable[65536];
unsigned short gbtable[65536];



/* builds some helper tables for doing color conversions */
void generate_conversion_tables(void)
{
   int r, g, b;
   int cr, cg, cb;

   /* this table combines a 16 bit r+g value into a al_screen pixel */
   for (r=0; r<256; r++) {
      cr = (r&31) * (r>>5) * 255/217;
      for (g=0; g<256; g++) {
	 cg = (g&31) * (g>>5) * 255/217;
	 rgtable[r+g*256] = al_make_color(cr, cg, 0);
      }
   }

   /* this table combines a 16 bit b+r value into a al_screen pixel */
   for (b=0; b<256; b++) {
      cb = (b&31) * (b>>5) * 255/217;
      for (r=0; r<256; r++) {
	 cr = (r&31) * (r>>5) * 255/217;
	 brtable[b+r*256] = al_make_color(0, 0, cb) | (al_make_color(cr, 0, 0) << 16);
      }
   }

   /* this table combines a 16 bit g+b value into a al_screen pixel */
   for (g=0; g<256; g++) {
      cg = (g&31) * (g>>5) * 255/217;
      for (b=0; b<256; b++) {
	 cb = (b&31) * (b>>5) * 255/217;
	 gbtable[g+b*256] = al_make_color(0, cg, cb);
      }
   }
}



/* copies from our magic format data onto a normal Allegro al_screen bitmap */
void blit_magic_format_to_screen(AL_BITMAP *bmp)
{
   unsigned long addr;
   unsigned long *data;
   unsigned long in1, in2, in3;
   unsigned long out1, out2;
   int x, y;

   /* Warning: this function contains some rather hairy optimisations :-)
    * The fastest way to copy large amounts of data is in aligned 32 bit 
    * chunks. If we expand it out to process 4 pixels per cycle, we can 
    * do this by reading 12 bytes (4 pixels) from the 24 bit source data, 
    * and writing 8 bytes (4 pixels) to the 16 bit destination. Then the 
    * only problem is how to convert our 12 bytes of data into a suitable 
    * 8 byte format, once we've got it loaded into registers. This is done 
    * by some lookup tables, which are arranged so they can process 2 color 
    * components in a single lookup, and allow me to precalculate the 
    * al_make_color() operation.
    *
    * Warning #2: this code is non endianess-safe. It will not port to
    * a big-endian platform without some major rewriting.
    *
    * Here is a (rather confusing) attempt to diagram the logic of the
    * lookup table lighting conversion from 24 to 16 bit format:
    *
    *
    *  inputs: |     (dword 1)     |     (dword 2)     |     (dword 3)     |
    *  pixels: |   (pixel1)   |   (pixel2)   |   (pixel3)   |   (pixel4)   |
    *  bytes:  | r1   g1   b1   r2   g2   b2   r3   g3   b3   r4   g4   b4 |
    *          |    |         |         |         |         |         |    |
    *  lookup: | rgtable   brtable   gbtable   rgtable   brtable   gbtable |
    *          |    |         |         |         |         |         |    |
    *  pixels: |   (pixel1)   |   (pixel2)   |   (pixel3)   |   (pixel4)   |
    *  outputs |          (dword 1)          |          (dword 2)          |
    *
    *
    * For reference, here is the original, non-optimised but much easier
    * to understand version of this routine:
    *
    *    _farsetsel(al_screen->seg);
    * 
    *    for (y=0; y<AL_SCREEN_H; y++) {
    *       addr = bmp_write_line(al_screen, y);
    * 
    *       for (x=0; x<AL_SCREEN_W; x++) {
    *          r = bmp->al_draw_line[y][x*3];
    *          g = bmp->al_draw_line[y][x*3+1];
    *          b = bmp->al_draw_line[y][x*3+2];
    * 
    *          r = (r&31) * (r>>5) * 255/217;
    *          g = (g&31) * (g>>5) * 255/217;
    *          b = (b&31) * (b>>5) * 255/217;
    * 
    *          _farnspokew(addr, al_make_color(r, g, b));
    * 
    *          addr += 2;
    *       }
    *    }
    */

   bmp_select(al_screen);

   for (y=0; y<AL_SCREEN_H; y++) {
      addr = bmp_write_line(al_screen, y);
      data = (unsigned long *)bmp->al_draw_line[y];

      for (x=0; x<AL_SCREEN_W/4; x++) {
	 in1 = *(data++);
	 in2 = *(data++);
	 in3 = *(data++);

	 /* trust me, this does make sense, really :-) */
	 out1 = rgtable[in1&0xFFFF] | 
		brtable[in1>>16] | 
		(gbtable[in2&0xFFFF] << 16);

	 out2 = rgtable[in2>>16] | 
		brtable[in3&0xFFFF] | 
		(gbtable[in3>>16] << 16);

	 bmp_write32(addr, out1);
	 bmp_write32(addr+4, out2);

	 addr += 8;
      }
   }

   bmp_unwrite_line(al_screen);
}



int main(int argc, char *argv[])
{
   AL_BITMAP *tmp;
   AL_PALETTE pal;
   char buf[256];
   int x, y, xc, yc, xl, yl, c, l;

   allegro_init();
   al_install_keyboard();
   al_install_timer();
   al_install_mouse();
   al_set_color_conversion(AL_COLORCONV_NONE);

   /* set a 15 or 16 bpp video mode */
   al_set_color_depth(16);
   if (al_set_gfx_mode(AL_GFX_AUTODETECT, 640, 480, 0, 0) != 0) {
      al_set_color_depth(15);
      if (al_set_gfx_mode(AL_GFX_AUTODETECT, 640, 480, 0, 0) != 0) {
	 al_set_gfx_mode(AL_GFX_NONE, 0, 0, 0, 0);
	 al_show_message("Error setting a 15 or 16 bpp 640x480 video mode\n%s\n", al_error);
	 return 1;
      }
   }

   /* create the double buffer, 8 bpp and three times as wide as the al_screen */
   buffer = al_create_bitmap_ex(8, AL_SCREEN_W*3, AL_SCREEN_H);

   /* load the first picture */
   al_replace_filename(buf, argv[0], "allegro.pcx", sizeof(buf));
   tmp = al_load_bitmap(buf, pal);
   if (!tmp) {
      al_destroy_bitmap(buffer);
      al_set_gfx_mode(AL_GFX_NONE, 0, 0, 0, 0);
      al_show_message("Error reading %s!\n", buf);
      return 1;
   }

   /* convert it into our special format */
   image1 = get_magic_bitmap_format(tmp, pal);
   al_destroy_bitmap(tmp);

   /* load the second picture */
   al_replace_filename(buf, argv[0], "mysha.pcx", sizeof(buf));
   tmp = al_load_bitmap(buf, pal);
   if (!tmp) {
      al_destroy_bitmap(buffer);
      al_destroy_bitmap(image1);
      al_set_gfx_mode(AL_GFX_NONE, 0, 0, 0, 0);
      al_show_message("Error reading %s!\n", buf);
      return 1;
   }

   /* convert it into our special format */
   image2 = get_magic_bitmap_format(tmp, pal);
   al_destroy_bitmap(tmp);

   /* create the light map image */
   lightmap = create_light_graphic();

   /* create our custom color blending map, which does translucency in the
    * bottom five bits and adds the light levels in the top three bits.
    * This version just does a 50% translucency if you are drawing sprites
    * with it, but you could easily make other color maps for different 
    * alpha levels, or for doing additive color, which can work happily
    * in parallel with the light blending.
    */
   al_color_map = malloc(sizeof(AL_COLOR_MAP));

   for (x=0; x<256; x++) {
      for (y=0; y<256; y++) {
	 xc = x&31;
	 yc = y&31;

	 xl = x>>5;
	 yl = y>>5;

	 if (xc)
	    c = (xc+yc)/2;
	 else
	    c = yc;

	 l = xl+yl;
	 if (l > 7)
	    l = 7;

	 al_color_map->data[x][y] = c | (l<<5);
      }
   }

   /* generate tables for converting pixels from magic->al_screen format */
   generate_conversion_tables();

   /* display the animation */
   while (!al_key_pressed()) {
      al_poll_mouse();

      al_clear_bitmap(buffer);

      /* we can draw the graphics using normal calls, just as if they were
       * regular 256 color images. Everything is just three times as wide 
       * as it would usually be, so we need to make sure that we only ever
       * draw to an x coordinate that is a multiple of three (otherwise
       * all the colors would get shifted out of phase with each other).
       */
      al_blit(image1, buffer, 0, 0, 0, al_retrace_count%(AL_SCREEN_H+image1->h)-image1->h, image1->w, image1->h);
      al_blit(image2, buffer, 0, 0, buffer->w-image2->w, buffer->h-(al_retrace_count*2/3)%(AL_SCREEN_H+image2->h), image2->w, image2->h);

      /* now we overlay translucent graphics and lights. Having set up a
       * suitable color blending table, these can be done at the same time,
       * either drawing a series of images some of which are translucent
       * sprites and some of which are light maps, or if we want, we can
       * just draw a single bitmap containing both color and light data 
       * with a single call, like this! You could also use graphics 
       * primitives like al_draw_circle_fill() to draw the lights, as long as you
       * do it in a translucent mode.
       */
      al_draw_trans_sprite(buffer, lightmap, (al_mouse_x-lightmap->w/6)*3, al_mouse_y-lightmap->h/2);

      /* this function is the al_key to the whole thing, converting from our
       * weird 5+3 interleaved format into a regular hicolor pixel that
       * can be displayed on your monitor.
       */
      blit_magic_format_to_screen(buffer);
   }

   al_clear_keybuf();

   al_destroy_bitmap(buffer);
   al_destroy_bitmap(image1);
   al_destroy_bitmap(image2);
   al_destroy_bitmap(lightmap);

   free(al_color_map);

   return 0;
}

AL_END_OF_MAIN();




See more files for this project here

Allegro game programming library

Allegro is a cross-platform library intended for use in computer games and other types of multimedia programming.

Project homepage: http://sourceforge.net/projects/alleg
Programming language(s): Assembly,C,Shell Script
License: other

  allegro.pcx
  ex12bit.c
  ex3buf.c
  ex3d.c
  exaccel.c
  exalpha.c
  example.dat
  example.h
  examples.txt
  exbitmap.c
  exblend.c
  excamera.c
  excolmap.c
  excustom.c
  exdata.c
  exdbuf.c
  exdodgy.c
  exexedat.c
  exfixed.c
  exflame.c
  exflip.c
  exgui.c
  exhello.c
  exjoy.c
  exkeys.c
  exlights.c
  exmem.c
  exmidi.c
  exmouse.c
  expal.c
  expat.c
  exquat.c
  exrgbhsv.c
  exsample.c
  exscale.c
  exscn3d.c
  exscroll.c
  exshade.c
  exspline.c
  exsprite.c
  exstars.c
  exstream.c
  exswitch.c
  extimer.c
  extrans.c
  extruec.c
  exunicod.c
  exupdate.c
  exxfade.c
  exzbuf.c
  mysha.pcx
  planet.pcx
  running.dat
  running.h
  unifont.dat