Show ex12bit.c syntax highlighted
/*
* Example program for the Allegro library, by Richard Mitton.
*
* or "How to get a 12-bit mode on an 8-bit card"
*
* This program sets up a 12-bit mode on any 8-bit card, by setting up
* a 256-colour palette that will fool the eye into grouping two 8-bit
* pixels into one 12-bit pixel.
*
* It's quite simple (sort of). You make your 256-colour palette with
* all the combinations of blue and green, assuming green ranges from
* 0-15 and blue from 0-14. This takes up 16x15=240 colours. This leaves
* 16 colours to use as red (red ranges from 0-15).
*
* Then you put your green/blue in one pixel, and your red in the pixel
* next to it. The eye gets fooled into thinking it's all one pixel.
*
* It's all very simple really. Honest.
*
* To start with, you set a normal 256 color VESA mode, and construct a
* special palette for it. But then comes the trick: you need to write
* to a set of two adjacent pixels to form a single 12 bit dot. Two eight
* bit pixels is the same as one 16 bit pixel, so after setting the video
* mode you need to hack the al_screen bitmap about, halving the width and
* changing it to use the 16 bit drawing code. Then, once you have packed
* a color into the correct format (using the makecol12() function below),
* any of the normal Allegro drawing functions can be used with this 12
* bit display!
*
* Things to note:
*
* The horizontal width is halved, so you get resolutions like 320x480,
* 400x600, and 512x768.
*
* Because each dot is spread over two actual pixels, the display will
* be darker than in a normal video mode.
*
* Any bitmap data will obviously need converting to the correct 12
* bit format: regular 15 or 16 bit images won't display correctly...
*
* Although this works like a truecolor mode, it is actually using a
* 256 color palette, so palette fades are still possible!
*
* Note: This code only works in linear al_screen modes (don't try Mode-X).
*/
#include "allegro.h"
/* declare the al_screen size and mask colour we will use */
#define GFXW 320
#define GFXH 480
#define AL_MASK_COLOR_12 0xFFE0
/* these are specific to this example. They say how big the balls are */
#define BALLW 12
#define BALLH 24
#define BIGBALLW 20
#define BIGBALLH 40
#define MESSAGE_STR "Allegro"
typedef struct
{
fixed x, y;
int c;
} POINT_T;
/* these functions can be used in any 12-bit program */
int makecol12(int r, int g, int b);
void set_12bit_palette();
AL_BITMAP *create_bitmap_12(int w, int h);
/* these functions are just for this example */
void blur_12(AL_BITMAP *bmp, AL_BITMAP *back);
void rgb_scales_12(AL_BITMAP *bmp, int ox, int oy, int w, int h);
POINT_T *make_points(int *numpoints, char *msg);
AL_BITMAP *make_ball(int w, int h, int br, int bg, int bb);
/* construct the magic palette that makes it all work */
void set_12bit_palette(void)
{
int r, g, b;
AL_PALETTE pal;
for (b=0; b<15; b++) {
for (g=0; g<16; g++) {
pal[b*16+g].r = 0;
pal[b*16+g].g = g*63/15;
pal[b*16+g].b = b*63/15;
}
}
for (r=0; r<16; r++) {
pal[r+240].r = r*63/15;
pal[r+240].g = 0;
pal[r+240].b = 0;
}
al_set_palette(pal);
}
/* the other magic routine - use this to make colours instead of al_make_color */
int makecol12(int r, int g, int b)
{
/* returns a 16-bit integer - here's the format:
*
* 0xARBG - where A=0xf (reserved, if you like),
* R=red (0-15)
* B=blue (0-14)
* G=green (0-15)
*/
r = r*16/256;
g = g*16/256;
b = b*16/256 - 1;
if (b < 0)
b = 0;
return (r << 8) | (b << 4) | g | 0xF000;
}
/* extract red component from color */
int getr12(int color)
{
return (color >> 4) & 0xF0;
}
/* extract green component from color */
int getg12(int color)
{
return (color << 4) & 0xF0;
}
/* extract blue component from color */
int getb12(int color)
{
return (color & 0xF0);
}
/* use this instead of al_create_bitmap, because the vtable needs changing
* so that the drawing functions will use the 16-bit functions.
*/
AL_BITMAP *create_bitmap_12(int w, int h)
{
AL_BITMAP *bmp;
bmp = al_create_bitmap_ex(16, w, h);
if (bmp) {
bmp->vtable->color_depth = 12;
bmp->vtable->mask_color = AL_MASK_COLOR_12;
}
return bmp;
}
/* this merges 'bmp' into 'back'. This is how the trails work */
void blur_12(AL_BITMAP *bmp, AL_BITMAP *back)
{
int x, y, r1, g1, b1, r2, g2, b2, c1, c2;
for (y=0; y<bmp->h; y++) {
unsigned short *backline = (unsigned short *)(back->al_draw_line[y]);
unsigned short *bmpline = (unsigned short *)(bmp->al_draw_line[y]);
for (x=0; x<bmp->w; x++) {
/* first get the pixel from each bitmap, then move the first
* colour value slightly towards the second.
*/
c1 = bmpline[x];
c2 = backline[x];
r1 = c1 & 0xF00;
r2 = c2 & 0xF00;
if (r1 < r2)
c1 += 0x100;
else if (r1 > r2)
c1 -= 0x100;
b1 = c1 & 0xF0;
b2 = c2 & 0xF0;
if (b1 < b2)
c1 += 0x10;
else if (b1 > b2)
c1 -= 0x10;
g1 = c1 & 0x0F;
g2 = c2 & 0x0F;
if (g1 < g2)
c1 += 0x01;
else if (g1 > g2)
c1 -= 0x01;
/* then put it back in the bitmap */
bmpline[x] = c1;
}
}
}
/* generates some nice AL_RGB scales onto the specified bitmap */
void rgb_scales_12(AL_BITMAP *bmp, int ox, int oy, int w, int h)
{
int x, y;
for (y=0; y<h; y++)
for (x=0; x<w; x++)
al_put_pixel(bmp, ox+x, oy+y, makecol12(x*256/w, y*256/h, 0));
for (y=0; y<h; y++)
for (x=0; x<w; x++)
al_put_pixel(bmp, ox+x+w, oy+y, makecol12(x*256/w, 0, y*256/h));
for (y=0; y<h; y++)
for (x=0; x<w; x++)
al_put_pixel(bmp, ox+x, oy+y+h, makecol12(0, x*256/w, y*256/h));
for (y=0; y<h; y++)
for (x=0; x<w; x++)
al_put_pixel(bmp, ox+x+w, oy+y+h, makecol12(x*128/w+y*128/h,
x*128/w+y*128/h,
x*128/w+y*128/h));
}
/* turns the string in 'msg' into a series of 2D points. These can then
* be drawn with the vector balls.
*/
POINT_T *make_points(int *numpoints, char *msg)
{
AL_BITMAP *bmp;
POINT_T *points;
int n, x, y;
bmp = al_create_bitmap_ex(8, al_text_length(al_font_8x8, msg), al_text_height(al_font_8x8));
al_clear_bitmap(bmp);
al_put_text(bmp, al_font_8x8, msg, 0, 0, 1);
/* first, count how much memory we will need to reserve */
n = 0;
for (y=0; y<bmp->h; y++)
for (x=0; x<bmp->w; x++)
if (al_get_pixel(bmp, x, y))
n++;
points = malloc(n * sizeof(POINT_T));
/* then redo it all, but actually store the points this time */
n = 0;
for (y=0; y<bmp->h; y++) {
for (x=0; x<bmp->w; x++) {
if (al_get_pixel(bmp, x, y)) {
points[n].x = al_int_to_fix(x - bmp->w/2) * 6;
points[n].y = al_int_to_fix(y - bmp->h/2) * 12;
points[n].c = rand() % 4;
n++;
}
}
}
*numpoints = n;
al_destroy_bitmap(bmp);
return points;
}
/* this draws a vector ball. br/bg/bg is the colour of the brightest spot */
AL_BITMAP *make_ball(int w, int h, int br, int bg, int bb)
{
AL_BITMAP *bmp;
int r, rx, ry;
bmp = create_bitmap_12(w, h);
al_clear_to_color(bmp, AL_MASK_COLOR_12);
for (r=0; r<16; r++) {
rx = w*(15-r)/32;
ry = h*(15-r)/32;
al_draw_ellipse_fill(bmp, w/2, h/2, rx, ry, makecol12(br*r/15, bg*r/15, bb*r/15));
}
return bmp;
}
int main(int argc, char *argv[])
{
AL_BITMAP *rgbpic, *ball[4], *buffer, *bigball;
int x, r=0, g=0, b=0, numpoints, thispoint;
fixed xangle, yangle, zangle, newx, newy, newz;
AL_GFX_VTABLE *orig_vtable;
POINT_T *points;
AL_MATRIX m;
allegro_init();
al_install_keyboard();
/* first set your graphics mode as normal, except twice as wide because
* we are using 2-bytes per pixel, but the graphics card doesn't know this.
*/
if (al_set_gfx_mode(AL_GFX_AUTODETECT, GFXW*sizeof(short), GFXH, 0, 0) != 0) {
al_set_gfx_mode(AL_GFX_NONE, 0, 0, 0, 0);
al_show_message("Error setting %ix%ix12 (really %ix%ix8, but we fake it):\n%s\n", GFXW, GFXH, GFXW*(int)sizeof(short), GFXH, al_error);
return 1;
}
/* then set your magic palette. From now on you can't use the al_set_palette_color
* or al_set_palette functions or they will mess up this palette. You can
* still use the fade routines, if you make sure you fade back into this
* palette.
*/
set_12bit_palette();
/* then hack the vtable so it uses the 16-bit functions */
orig_vtable = al_screen->vtable;
#ifdef ALLEGRO_COLOR16
al_screen->vtable = &__linear_vtable16;
#endif
al_screen->vtable->color_depth = 12;
al_screen->vtable->mask_color = AL_MASK_COLOR_12;
al_screen->vtable->unwrite_bank = orig_vtable->unwrite_bank;
al_screen->w /= sizeof(short);
/* reset the clip window to it's new parameters */
al_set_clip(al_screen, 0, 0, al_screen->w-1, al_screen->h-1);
/* then generate 4 vector balls of different colours */
for (x=0; x<4; x++) {
switch (x) {
case 0: r = 255; g = 0; b = 0; break;
case 1: r = 0; g = 255; b = 0; break;
case 2: r = 0; g = 0; b = 255; break;
case 3: r = 255; g = 255; b = 0; break;
}
ball[x] = make_ball(BALLW, BALLH, r, g, b);
}
/* also make one big red vector ball */
bigball = make_ball(BIGBALLW, BIGBALLH, 255, 0, 0);
/* make the off-al_screen buffer that everything will be drawn onto */
buffer = create_bitmap_12(GFXW, GFXH);
/* convert the text message into the coordinates of the vector balls */
points = make_points(&numpoints, MESSAGE_STR);
/* create the background picture */
rgbpic = create_bitmap_12(GFXW, GFXH);
rgb_scales_12(rgbpic, 0, 0, GFXW/2, GFXH/2);
/* copy the background into the buffer */
al_blit(rgbpic, buffer, 0, 0, 0, 0, GFXW, GFXH);
xangle = yangle = zangle = 0;
/* put a message in the top-left corner */
al_text_mode(-1);
al_printf_text(rgbpic, al_font_8x8, 3, 3, makecol12(255, 255, 255), "%ix%i 12-bit colour on an 8-bit card", GFXW, GFXH);
al_printf_text(rgbpic, al_font_8x8, 3, 13, makecol12(255, 255, 255), "(3840 colours at once!)");
while (!al_key_pressed()) {
/* first, draw some vector balls moving in a al_draw_circle round the edge */
for (x=0; x<al_int_to_fix(256); x += al_int_to_fix(32)) {
al_blit_masked(bigball, buffer, 0, 0,
al_fix_to_int(150 * al_fix_cos(xangle+x)) + GFXW/2 - BALLW/2,
al_fix_to_int(200 * al_fix_sin(xangle+x)) + GFXH/2 - BALLH/2,
BIGBALLW, BIGBALLH);
}
/* rotate the vector balls */
al_get_rotation_matrix(&m, xangle, yangle, zangle);
for (thispoint=0; thispoint<numpoints; thispoint++) {
al_apply_matrix(&m, points[thispoint].x,
points[thispoint].y,
0,
&newx, &newy, &newz);
al_blit_masked(ball[points[thispoint].c], buffer, 0,0,
al_fix_to_int(newx) + GFXW/2,
al_fix_to_int(newy) + GFXH/2, BALLW, BALLH);
}
/* then blur the buffer so it fades into the background picture */
blur_12(buffer, rgbpic);
/* finally copy everything to the al_screen */
al_blit(buffer, al_screen, 0, 0, 0, 0, GFXW,GFXH);
/* rotate it a bit more */
xangle += al_int_to_fix(1);
yangle += al_int_to_fix(1);
zangle += al_int_to_fix(1);
}
al_clear_keybuf();
/* clean it all up */
for (x=0; x<4; x++)
al_destroy_bitmap(ball[x]);
al_destroy_bitmap(bigball);
free(points);
al_destroy_bitmap(rgbpic);
al_destroy_bitmap(buffer);
al_screen->vtable = orig_vtable;
al_fade_out(4);
return 0;
}
AL_END_OF_MAIN();
See more files for this project here