File: ex12bit.c

package info (click to toggle)
allegro4 2%3A4.0.1-1
  • links: PTS
  • area: main
  • in suites: woody
  • size: 17,052 kB
  • ctags: 12,972
  • sloc: ansic: 109,525; asm: 16,672; cpp: 3,221; sh: 1,761; makefile: 556; pascal: 105; perl: 73
file content (447 lines) | stat: -rw-r--r-- 11,383 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
/*
 *    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 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 screen modes (don't try Mode-X).
 */


#include "allegro.h"



/* 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();
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; y<bmp->h; y++) {
      unsigned short *backline = (unsigned short *)(back->line[y]);
      unsigned short *bmpline = (unsigned short *)(bmp->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 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; y<h; y++)
      for (x=0; x<w; x++)
	 putpixel(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++)
	 putpixel(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++)
	 putpixel(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++)
	 putpixel(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)
{
   BITMAP *bmp;
   POINT_T *points;
   int n, x, y;

   bmp = create_bitmap_ex(8, text_length(font, msg), text_height(font));
   clear_bitmap(bmp);
   textout(bmp, font, 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 (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; y<bmp->h; y++) {
      for (x=0; x<bmp->w; 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 = 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(int argc, char *argv[])
{
   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;

   allegro_init();
   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*sizeof(short), 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*(int)sizeof(short), 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(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 */
   text_mode(-1);
   textprintf(rgbpic, font, 3, 3, makecol12(255, 255, 255), "%ix%i 12-bit colour on an 8-bit card", GFXW, GFXH);
   textprintf(rgbpic, font, 3, 13, makecol12(255, 255, 255), "(3840 colours at once!)");

   while (!keypressed()) {
      /* first, draw some vector balls moving in a circle round the edge */
      for (x=0; x<itofix(256); x += itofix(32)) {
	 masked_blit(bigball, buffer, 0, 0,
		     fixtoi(150 * fixcos(xangle+x)) + GFXW/2 - BALLW/2,
		     fixtoi(200 * fixsin(xangle+x)) + GFXH/2 - BALLH/2,
		     BIGBALLW, BIGBALLH);
      }

      /* rotate the vector balls */

      get_rotation_matrix(&m, xangle, yangle, zangle);

      for (thispoint=0; thispoint<numpoints; thispoint++) {
	 apply_matrix(&m, points[thispoint].x,
			  points[thispoint].y,
			  0,
			  &newx, &newy, &newz);

	 masked_blit(ball[points[thispoint].c], buffer, 0,0,
		     fixtoi(newx) + GFXW/2,
		     fixtoi(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 screen */
      blit(buffer, screen, 0, 0, 0, 0, GFXW,GFXH);

      /* rotate it a bit more */
      xangle += itofix(1);
      yangle += itofix(1);
      zangle += itofix(1);
   }

   clear_keybuf();

   /* clean it all up */
   for (x=0; x<4; x++)
      destroy_bitmap(ball[x]);

   destroy_bitmap(bigball);
   free(points);

   destroy_bitmap(rgbpic);
   destroy_bitmap(buffer);
   
   screen->vtable = orig_vtable;
   fade_out(4);

   return 0;
}

END_OF_MAIN();