/*
* Example program for the Allegro library, by Richard Mitton.
*
* 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. In order
* to do this, 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.
*
* The example starts setting a normal 256 color 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 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!
*
*
- This code only works in linear screen modes (don't try
* Mode-X).
*/
#include
/* declare the screen size and mask colour we will use */
#define GFXW 320
#define GFXH 480
#define 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(void);
BITMAP *create_bitmap_12(int w, int h);
/* these functions are just for this example */
void blur_12(BITMAP *bmp, BITMAP *back);
void rgb_scales_12(BITMAP *bmp, int ox, int oy, int w, int h);
POINT_T *make_points(int *numpoints, char *msg);
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;
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;
}
set_palette(pal);
}
/* the other magic routine - use this to make colours instead of makecol */
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 create_bitmap, because the vtable needs changing
* so that the drawing functions will use the 16-bit functions.
*/
BITMAP *create_bitmap_12(int w, int h)
{
BITMAP *bmp;
bmp = create_bitmap_ex(16, w, h);
if (bmp) {
bmp->vtable->color_depth = 12;
bmp->vtable->mask_color = MASK_COLOR_12;
}
return bmp;
}
/* this merges 'bmp' into 'back'. This is how the trails work */
void blur_12(BITMAP *bmp, BITMAP *back)
{
int x, y, r1, g1, b1, r2, g2, b2, c1, c2;
for (y=0; yh; y++) {
unsigned short *backline = (unsigned short *)(back->line[y]);
unsigned short *bmpline = (unsigned short *)(bmp->line[y]);
for (x=0; xw; 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 RGB scales onto the specified bitmap */
void rgb_scales_12(BITMAP *bmp, int ox, int oy, int w, int h)
{
int x, y;
for (y=0; yh; y++)
for (x=0; xw; x++)
if (getpixel(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; yh; y++) {
for (x=0; xw; x++) {
if (getpixel(bmp, x, y)) {
points[n].x = itofix(x - bmp->w/2) * 6;
points[n].y = itofix(y - bmp->h/2) * 12;
points[n].c = AL_RAND() % 4;
n++;
}
}
}
*numpoints = n;
destroy_bitmap(bmp);
return points;
}
/* this draws a vector ball. br/bg/bg is the colour of the brightest spot */
BITMAP *make_ball(int w, int h, int br, int bg, int bb)
{
BITMAP *bmp;
int r, rx, ry;
bmp = create_bitmap_12(w, h);
clear_to_color(bmp, MASK_COLOR_12);
for (r=0; r<16; r++) {
rx = w*(15-r)/32;
ry = h*(15-r)/32;
ellipsefill(bmp, w/2, h/2, rx, ry, makecol12(br*r/15, bg*r/15, bb*r/15));
}
return bmp;
}
int main(void)
{
BITMAP *rgbpic, *ball[4], *buffer, *bigball;
int x, r=0, g=0, b=0, numpoints, thispoint;
fixed xangle, yangle, zangle, newx, newy, newz;
GFX_VTABLE *orig_vtable;
POINT_T *points;
MATRIX m;
if (allegro_init() != 0)
return 1;
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 (set_gfx_mode(GFX_AUTODETECT, GFXW * 2, GFXH, 0, 0) != 0) {
set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
allegro_message("Error setting %ix%ix12 (really %ix%ix8, but we fake "
"it):\n%s\n", GFXW, GFXH, GFXW* 2, GFXH,
allegro_error);
return 1;
}
/* then set your magic palette. From now on you can't use the set_color
* or 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 = screen->vtable;
#ifdef ALLEGRO_COLOR16
screen->vtable = &__linear_vtable16;
#endif
screen->vtable->color_depth = 12;
screen->vtable->mask_color = MASK_COLOR_12;
screen->vtable->unwrite_bank = orig_vtable->unwrite_bank;
screen->w /= sizeof(short);
/* reset the clip window to it's new parameters */
set_clip_rect(screen, 0, 0, screen->w-1, 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-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 */
blit(rgbpic, buffer, 0, 0, 0, 0, GFXW, GFXH);
xangle = yangle = zangle = 0;
/* put a message in the top-left corner */
textprintf_ex(rgbpic, font, 3, 3, makecol12(255, 255, 255), -1,
"%ix%i 12-bit colour on an 8-bit card", GFXW, GFXH);
textprintf_ex(rgbpic, font, 3, 13, makecol12(255, 255, 255), -1,
"(3840 colours at once!)");
while (!keypressed()) {
/* first, draw some vector balls moving in a circle round the edge */
for (x=0; xvtable = orig_vtable;
fade_out(4);
return 0;
}
END_OF_MAIN()