📄 exlights.c
字号:
/*
* 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 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 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 */
BITMAP *buffer;
/* the two moving bitmap images */
BITMAP *image1;
BITMAP *image2;
/* the light map, drawn wherever the mouse pointer is */
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 */
BITMAP *get_magic_bitmap_format(BITMAP *orig, PALETTE pal)
{
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 = create_bitmap_ex(8, orig->w*3, orig->h);
/* store info about the original bitmap format */
bpp = bitmap_color_depth(orig);
select_palette(pal);
/* convert the data */
for (y=0; y<orig->h; y++) {
for (x=0; x<orig->w; x++) {
c = getpixel(orig, x, y);
r = getr_depth(bpp, c);
g = getg_depth(bpp, c);
b = getb_depth(bpp, c);
bmp->line[y][x*3] = r*31/255 | AMBIENT_LIGHT;
bmp->line[y][x*3+1] = g*31/255 | AMBIENT_LIGHT;
bmp->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 */
BITMAP *create_light_graphic(void)
{
BITMAP *bmp;
int x, y;
int dx, dy;
int dist, dir;
int r, g, b;
bmp = 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 = fixtoi(fixsqrt(itofix(MID(-32768, dx*dx+dy*dy, 32767))));
dir = fixtoi(fixatan2(itofix(dy), itofix(dx)) + itofix(128));
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->line[y][x*3] = MID(0, r, 7) << 5;
bmp->line[y][x*3+1] = MID(0, g, 7) << 5;
bmp->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->line[y][(x)*3] &= 0xE0; \
bmp->line[y][(x)*3+1] &= 0xE0; \
bmp->line[y][(x)*3+2] &= 0xE0; \
\
bmp->line[y][(x)*3] |= r; \
bmp->line[y][(x)*3+1] |= g; \
bmp->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;
}
#ifdef ALLEGRO_LITTLE_ENDIAN
/* 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 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] = makecol(cr, cg, 0);
}
}
/* this table combines a 16 bit b+r value into a 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] = makecol(0, 0, cb) | (makecol(cr, 0, 0) << 16);
}
}
/* this table combines a 16 bit g+b value into a 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] = makecol(0, cg, cb);
}
}
}
/* copies from our magic format data onto a normal Allegro screen bitmap */
void blit_magic_format_to_screen(BITMAP *bmp)
{
uintptr_t addr;
uint32_t *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
* makecol() operation.
*
* Warning #2: this code assumes little-endianess.
*
* Here is a (rather confusing) attempt to diagram the logic of the
* lookup table lighting conversion from 24 to 16 bit format in little-
* endian 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(screen->seg);
*
* for (y=0; y<SCREEN_H; y++) {
* addr = bmp_write_line(screen, y);
*
* for (x=0; x<SCREEN_W; x++) {
* r = bmp->line[y][x*3];
* g = bmp->line[y][x*3+1];
* b = bmp->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, makecol(r, g, b));
*
* addr += 2;
* }
* }
*/
bmp_select(screen);
for (y=0; y<SCREEN_H; y++) {
addr = bmp_write_line(screen, y);
data = (uint32_t *)bmp->line[y];
for (x=0; x<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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -