1 /*
2  *    Example program for the Allegro library, by Shawn Hargreaves.
3  *
4  *    This program demonstrates how to write directly to video memory.
5  *    It implements a simple fire effect, first by calling getpixel() and
6  *    putpixel(), then by accessing video memory directly a byte at a
7  *    time, and finally using block memory copy operations.
8  */
9 
10 
11 #include <allegro.h>
12 
13 
14 
15 /* The fire is formed from several 'hotspots' which are moved randomly
16  * across the bottom of the screen.
17  */
18 #define FIRE_HOTSPOTS   48
19 
20 int hotspot[FIRE_HOTSPOTS];
21 
22 unsigned char *temp;
23 
24 
25 
26 /* This function updates the bottom line of the screen with a pattern
27  * of varying intensities which are then moved upwards and faded out
28  * by the code in main().
29  */
draw_bottom_line_of_fire(void)30 void draw_bottom_line_of_fire(void)
31 {
32    int c, c2;
33 
34    /* zero the buffer */
35    for (c=0; c<SCREEN_W; c++)
36       temp[c] = 0;
37 
38    for (c=0; c<FIRE_HOTSPOTS; c++) {
39       /* display the hotspots */
40       for (c2=hotspot[c]-20; c2<hotspot[c]+20; c2++)
41 	 if ((c2 >= 0) && (c2 < SCREEN_W))
42 	    temp[c2] = MIN(temp[c2] + 20-ABS(hotspot[c]-c2), 192);
43 
44       /* move the hotspots */
45       hotspot[c] += (AL_RAND() & 7) - 3;
46       if (hotspot[c] < 0)
47 	 hotspot[c] += SCREEN_W;
48       else
49 	 if (hotspot[c] >= SCREEN_W)
50 	    hotspot[c] -= SCREEN_W;
51    }
52 
53    /* display the buffer */
54    for (c=0; c<SCREEN_W; c++)
55       putpixel(screen, c, SCREEN_H-1, temp[c]);
56 }
57 
58 
59 
main(void)60 int main(void)
61 {
62    PALETTE palette;
63    uintptr_t address;
64    int x, y, c;
65 
66    if (allegro_init() != 0)
67       return 1;
68    install_keyboard();
69    if (set_gfx_mode(GFX_AUTODETECT, 320, 200, 0, 0) != 0) {
70       if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0) != 0) {
71 	 allegro_message("Error setting graphics mode\n%s\n", allegro_error);
72 	 return 1;
73       }
74    }
75 
76    temp = (unsigned char *)malloc(sizeof(unsigned char) * SCREEN_W);
77 
78    if (!temp) {
79       set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
80       allegro_message("Not enough memory? This is a joke right!?!\n");
81       return 0;
82    }
83 
84    for (c=0; c<FIRE_HOTSPOTS; c++)
85       hotspot[c] = AL_RAND() % SCREEN_W;
86 
87    /* fill our palette with a gradually altering sequence of colors */
88    for (c=0; c<64; c++) {
89       palette[c].r = c;
90       palette[c].g = 0;
91       palette[c].b = 0;
92    }
93    for (c=64; c<128; c++) {
94       palette[c].r = 63;
95       palette[c].g = c-64;
96       palette[c].b = 0;
97    }
98    for (c=128; c<192; c++) {
99       palette[c].r = 63;
100       palette[c].g = 63;
101       palette[c].b = c-128;
102    }
103    for (c=192; c<256; c++) {
104       palette[c].r = 63;
105       palette[c].g = 63;
106       palette[c].b = 63;
107    }
108 
109    set_palette(palette);
110 
111    textout_ex(screen, font, "Using get/putpixel()", 0, 0, makecol(255,255,255), makecol(0, 0, 0));
112 
113    /* using getpixel() and putpixel() is slow :-) */
114    while (!keypressed()) {
115       acquire_screen();
116 
117       draw_bottom_line_of_fire();
118 
119       for (y=64; y<SCREEN_H-1; y++) {
120 	 /* read line */
121 	 for (x=0; x<SCREEN_W; x++) {
122 	    c = getpixel(screen, x, y+1);
123 
124 	    if (c > 0)
125 	       c--;
126 
127 	    putpixel(screen, x, y, c);
128 	 }
129       }
130       release_screen();
131 
132       /* Hack to make sure keyboard input is noticed. */
133       rest(0);
134    }
135 
136    clear_keybuf();
137    textout_ex(screen, font, "Using direct memory writes", 0, 0, makecol(255,255,255), makecol(0, 0, 0));
138 
139    /* It is much faster if we access the screen memory directly. This
140     * time we read an entire line of the screen into our own buffer,
141     * modify it there, and then write the whole line back in one go.
142     * That is to avoid having to keep switching back and forth between
143     * different scanlines: if we only copied one pixel at a time, we
144     * would have to call bmp_write_line() for every single pixel rather
145     * than just twice per line.
146     */
147    while (!keypressed()) {
148       acquire_screen();
149       draw_bottom_line_of_fire();
150 
151       bmp_select(screen);
152 
153       for (y=64; y<SCREEN_H-1; y++) {
154 	 /* get an address for reading line y+1 */
155 	 address = bmp_read_line(screen, y+1);
156 
157 	 /* read line with farptr functions */
158 	 for (x=0; x<SCREEN_W; x++)
159 	    temp[x] = bmp_read8(address+x);
160 
161 	 /* adjust it */
162 	 for (x=0; x<SCREEN_W; x++)
163 	    if (temp[x] > 0)
164 	       temp[x]--;
165 
166 	 /* get an address for writing line y */
167 	 address = bmp_write_line(screen, y);
168 
169 	 /* write line with farptr functions */
170 	 for (x=0; x<SCREEN_W; x++)
171 	    bmp_write8(address+x, temp[x]);
172       }
173 
174       bmp_unwrite_line(screen);
175       release_screen();
176 
177       /* Hack to make sure keyboard input is noticed. */
178       rest(0);
179    }
180 
181    clear_keybuf();
182    textout_ex(screen, font, "Using block data transfers", 0, 0, makecol(255,255,255), makecol(0, 0, 0));
183 
184    /* It is even faster if we transfer the data in 32 bit chunks, rather
185     * than only one pixel at a time. This method may not work on really
186     * unusual machine architectures, but should be ok on just about
187     * anything that you are practically likely to come across.
188     */
189    while (!keypressed()) {
190       acquire_screen();
191       draw_bottom_line_of_fire();
192 
193       bmp_select(screen);
194 
195       for (y=64; y<SCREEN_H-1; y++) {
196 	 /* get an address for reading line y+1 */
197 	 address = bmp_read_line(screen, y+1);
198 
199 	 /* read line in 32 bit chunks */
200 	 for (x=0; x<SCREEN_W; x += sizeof(uint32_t))
201 	    *((uint32_t *)&temp[x]) = bmp_read32(address+x);
202 
203 	 /* adjust it */
204 	 for (x=0; x<SCREEN_W; x++)
205 	    if (temp[x] > 0)
206 	       temp[x]--;
207 
208 	 /* get an address for writing line y */
209 	 address = bmp_write_line(screen, y);
210 
211 	 /* write line in 32 bit chunks */
212 	 for (x=0; x<SCREEN_W; x += sizeof(uint32_t))
213 	    bmp_write32(address+x, *((uint32_t *)&temp[x]));
214       }
215 
216       bmp_unwrite_line(screen);
217       release_screen();
218 
219       /* Hack to make sure keyboard input is noticed. */
220       rest(0);
221    }
222 
223    free(temp);
224 
225    return 0;
226 }
227 
228 END_OF_MAIN()
229