Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_allegro_4.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 */
17
33
34#include "nilorea/n_common.h"
35#include "nilorea/n_log.h"
36#include "nilorea/n_allegro_4.h"
37
38#include <getopt.h>
39
40static int screenshot_mode = 0;
41static volatile int close_asked = 0;
42
43static void close_cb(void) {
44 close_asked = 1;
45}
46
47void usage(void) {
48 fprintf(stderr,
49 " -v version\n"
50 " -V log level: LOG_INFO, LOG_NOTICE, LOG_ERR, LOG_DEBUG\n"
51 " -s render one frame to ex_allegro_4.png and exit\n"
52 " -h help\n");
53}
54
55void process_args(int argc, char** argv) {
56 int getoptret = 0, log_level = LOG_ERR;
57 while ((getoptret = getopt(argc, argv, "hvsV:")) != EOF) {
58 switch (getoptret) {
59 case 'v':
60 fprintf(stderr, "Date de compilation : %s a %s.\n", __DATE__, __TIME__);
61 exit(0);
62 case 'V':
63 if (!strncmp("LOG_NULL", optarg, 8)) {
65 } else if (!strncmp("LOG_NOTICE", optarg, 10)) {
67 } else if (!strncmp("LOG_INFO", optarg, 8)) {
69 } else if (!strncmp("LOG_ERR", optarg, 7)) {
71 } else if (!strncmp("LOG_DEBUG", optarg, 9)) {
73 } else {
74 fprintf(stderr, "%s is not a valid log level\n", optarg);
75 exit(1);
76 }
77 break;
78 case 's':
80 break;
81 default:
82 case 'h':
83 usage();
84 exit(1);
85 }
86 }
88}
89
90/* A 64x64 sprite: a ball with an arrow on a mask colour background. */
91static BITMAP* make_sprite(void) {
92 BITMAP* spr = create_bitmap(64, 64);
93 if (!spr) return NULL;
95 circlefill(spr, 32, 32, 26, makecol(240, 200, 40));
96 circle(spr, 32, 32, 26, makecol(120, 80, 0));
97 triangle(spr, 32, 10, 22, 30, 42, 30, makecol(200, 30, 30));
98 rectfill(spr, 29, 28, 35, 52, makecol(200, 30, 30));
99 return spr;
100}
101
102/* Check pixel (x,y) of bmp against an expected rgb, logging a mismatch. */
103static int expect(BITMAP* bmp, int x, int y, int r, int g, int b, const char* what) {
104 int c = getpixel(bmp, x, y);
105 if (getr(c) != r || getg(c) != g || getb(c) != b) {
106 n_log(LOG_ERR, "%s: pixel %d,%d is %d,%d,%d, expected %d,%d,%d", what, x, y, getr(c), getg(c), getb(c), r, g, b);
107 return FALSE;
108 }
109 return TRUE;
110}
111
112static void draw_scene(BITMAP* buf, BITMAP* spr, double t) {
113 drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
114 clear_to_color(buf, makecol(20, 30, 60));
115
116 /* primitives */
117 rectfill(buf, 20, 20, 219, 119, makecol(60, 120, 60));
118 rect(buf, 20, 20, 219, 119, makecol(255, 255, 255));
119 line(buf, 20, 20, 219, 119, makecol(255, 255, 0));
120 circle(buf, 320, 70, 50, makecol(0, 200, 255));
121 ellipsefill(buf, 480, 70, 60, 30, makecol(150, 60, 200));
122
123 /* sprites */
124 draw_sprite(buf, spr, 20, 150);
125 draw_sprite_h_flip(buf, spr, 100, 150);
126 draw_sprite_v_flip(buf, spr, 180, 150);
127 stretch_sprite(buf, spr, 260, 150, 128, 64);
128 rotate_sprite(buf, spr, 420, 150, ftofix(t * 64.0));
129 pivot_sprite(buf, spr, 560, 182, 32, 32, itofix(64));
130
131 /* blending: alpha of the colour, then a constant opacity */
132 drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
134 rectfill(buf, 20, 240, 219, 319, makeacol(255, 0, 0, 128));
135 rectfill(buf, 120, 280, 319, 359, makeacol(0, 0, 255, 128));
136 set_trans_blender(0, 0, 0, 96);
137 draw_trans_sprite(buf, spr, 360, 260);
138 drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
139
140 /* direct pixel access, PMASK style */
141 for (int y = 0; y < 40; y++) {
142 uintptr_t addr = bmp_write_line(buf, 260 + y);
143 for (int x = 0; x < 40; x++) bmp_write32(addr + (uintptr_t)(480 + x) * 4, makecol(x * 6, y * 6, 128));
144 bmp_unwrite_line(buf);
145 }
146 for (int x = 0; x < 40; x++) putpixel(buf, 530 + x, 280, makecol(255, 255, 255));
147
148 /* text */
149 textprintf_ex(buf, font, 20, 400, makecol(255, 255, 255), -1, "Allegro 4 on Allegro 5: %dx%d, t=%.1f", SCREEN_W, SCREEN_H, t);
150 textprintf_centre_ex(buf, font, buf->w / 2, 430, makecol(0, 0, 0), makecol(255, 220, 0), " centred, with a background ");
151 textout_right_ex(buf, font, "right aligned", buf->w - 20, 460, makecol(180, 180, 255), -1);
152}
153
154int main(int argc, char** argv) {
155 process_args(argc, argv);
156
157 if (allegro_init() != 0) {
158 n_log(LOG_ERR, "allegro_init: %s", allegro_error);
159 return 1;
160 }
164 set_color_depth(32);
165 if (set_gfx_mode(GFX_OPENGL_WINDOWED, 640, 480, 0, 0) != 0) {
166 /* no display (headless CI): nothing to check, skip */
167 n_log(LOG_NOTICE, "set_gfx_mode: %s, skipping", allegro_error);
168 allegro_exit();
169 return 0;
170 }
171 set_window_title("ex_allegro_4");
173
174 BITMAP* spr = make_sprite();
176 if (!spr || !buf) {
177 n_log(LOG_ERR, "could not create the bitmaps");
178 return 1;
179 }
180
181 int ok = TRUE;
182 /* the sprite keeps the mask colour as a plain colour */
183 if (getpixel(spr, 0, 0) != bitmap_mask_color(spr)) {
184 n_log(LOG_ERR, "mask colour not kept: %08x", (unsigned)getpixel(spr, 0, 0));
185 ok = FALSE;
186 }
187 if (fixtoi(itofix(12)) != 12 || fixtoi(ftofix(2.5)) != 3) {
188 n_log(LOG_ERR, "fixed point conversions are off");
189 ok = FALSE;
190 }
191
192 double t = 0.0;
193 do {
194 draw_scene(buf, spr, t);
195 blit(buf, screen, 0, 0, 0, 0, buf->w, buf->h);
197 t += 0.02;
198 rest(10);
199 } while (!screenshot_mode && !key[KEY_ESC] && !close_asked);
200
201 /* masked draws leave the background, solid ones overwrite it */
202 ok = expect(buf, 21, 151, 20, 30, 60, "draw_sprite mask") && ok;
203 ok = expect(buf, 32, 182, 240, 200, 40, "draw_sprite body") && ok;
204 ok = expect(buf, 100, 30, 60, 120, 60, "rectfill") && ok;
205 ok = expect(buf, 485, 265, 30, 30, 128, "bmp_write32") && ok;
206 ok = expect(buf, 540, 280, 255, 255, 255, "putpixel") && ok;
207 /* 50% red over the background: (255+20)/2, (0+30)/2, (0+60)/2 */
208 int c = getpixel(buf, 50, 250);
209 if (abs(getr(c) - 137) > 2 || abs(getg(c) - 15) > 2 || abs(getb(c) - 30) > 2) {
210 n_log(LOG_ERR, "alpha blend: %d,%d,%d, expected about 137,15,30", getr(c), getg(c), getb(c));
211 ok = FALSE;
212 }
213
214 /* the fade trick: Allegro 4 takes at least one step off per frame, so a
215 * black overlay at opacity 1 ends on black, and opacity 64 takes
216 * ceil(60 * 65 / 256) = 16 off 60 in one go */
217 BITMAP* fade = create_bitmap(8, 8);
218 if (fade) {
219 clear_to_color(fade, makecol(60, 60, 60));
220 drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
222 rectfill(fade, 0, 0, 7, 7, makeacol(0, 0, 0, 64));
223 ok = expect(fade, 2, 2, 44, 44, 44, "fade step at opacity 64") && ok;
224 for (int i = 0; i < 255; i++) rectfill(fade, 0, 0, 7, 7, makeacol(0, 0, 0, 1));
225 drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
226 ok = expect(fade, 2, 2, 0, 0, 0, "fade to black at opacity 1") && ok;
227 destroy_bitmap(fade);
228 }
229
230 if (screenshot_mode && save_bitmap("ex_allegro_4.png", buf, NULL) != 0) {
231 n_log(LOG_ERR, "could not save ex_allegro_4.png");
232 ok = FALSE;
233 }
234
235 destroy_bitmap(buf);
236 destroy_bitmap(spr);
237 allegro_exit();
238 n_log(LOG_NOTICE, "ex_allegro_4: %s", ok ? "all checks passed" : "some checks FAILED");
239 return ok ? 0 : 1;
240}
static void usage(void)
static volatile int close_asked
static BITMAP * make_sprite(void)
static void close_cb(void)
static void draw_scene(BITMAP *buf, BITMAP *spr, double t)
static int expect(BITMAP *bmp, int x, int y, int r, int g, int b, const char *what)
static int screenshot_mode
void process_args(int argc, char **argv)
int main(void)
Definition ex_atlas.c:117
static int log_level
int getoptret
Definition ex_fluid.c:59
char * addr
int w
width in pixels
int h
height in pixels
int install_mouse(void)
start the input thread for the mouse
uintptr_t bmp_write_line(BITMAP *bmp, int y)
lock the bitmap and return the address of line y, 32 bit ARGB pixels
void draw_sprite_h_flip(BITMAP *bmp, BITMAP *sprite, int x, int y)
draw a sprite mirrored horizontally
void destroy_bitmap(BITMAP *bmp)
free a bitmap
void set_trans_blender(int r, int g, int b, int a)
blend with a constant opacity a (0-255), r, g and b are ignored
void allegro_exit(void)
shut everything down
char allegro_error[256]
last error message, as in Allegro 4
int set_close_button_callback(void(*proc)(void))
call proc when the window close button is pressed
void bmp_unwrite_line(BITMAP *bmp)
no-op, the bitmap is unlocked when next drawn with the GPU
BITMAP * create_bitmap(int width, int height)
create a w x h bitmap
#define SCREEN_H
screen height
int bitmap_mask_color(BITMAP *bmp)
the mask colour, magenta
int getr(int c)
red component
void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color)
draw a line
volatile char key[227]
TRUE while the key is held.
int save_bitmap(const char *filename, BITMAP *bmp, const RGB *pal)
save a bitmap
void textout_right_ex(BITMAP *bmp, const FONT *f, const char *s, int x, int y, int color, int bg)
draw a string ending at x
FONT * font
the default 8x8 font, valid after allegro_init()
void textprintf_ex(BITMAP *bmp, const FONT *f, int x, int y, int color, int bg, const char *format,...)
draw a formatted string
void drawing_mode(int mode, BITMAP *pattern, int x_anchor, int y_anchor)
set the drawing mode of primitives, pattern arguments are ignored
#define itofix(x)
int to fixed
#define DRAW_MODE_SOLID
drawing mode: plain copy
#define ftofix(x)
double to fixed
#define bmp_write32(addr, c)
write a 32 bit pixel at addr
int makeacol(int r, int g, int b, int a)
make a colour with alpha
BITMAP * screen
the screen, valid after set_gfx_mode()
void set_alpha_blender(void)
blend with the alpha channel of the source colour or sprite
#define GFX_OPENGL_WINDOWED
AllegroGL windowed: screen is shown on allegro_gl_flip() only.
#define DRAW_MODE_TRANS
drawing mode: blend with the current blender
int install_keyboard(void)
start the input thread for the keyboard
int set_gfx_mode(int card, int w, int h, int v_w, int v_h)
open a display of w x h pixels, or close it with GFX_TEXT
int allegro_init(void)
init the layer and Allegro 5
int install_timer(void)
no-op, timers are always available
void circle(BITMAP *bmp, int x, int y, int radius, int color)
draw a circle outline
void set_window_title(const char *name)
set the window title
int makecol(int r, int g, int b)
make an opaque colour
#define fixtoi(x)
fixed to int, rounded
void blit(BITMAP *source, BITMAP *dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height)
copy a rectangle
#define KEY_ESC
key code
void ellipsefill(BITMAP *bmp, int x, int y, int rx, int ry, int color)
draw a filled ellipse
void textprintf_centre_ex(BITMAP *bmp, const FONT *f, int x, int y, int color, int bg, const char *format,...)
draw a formatted string centred on x
int getb(int c)
blue component
#define SCREEN_W
screen width
int getg(int c)
green component
void draw_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y)
draw a sprite
void rotate_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, fixed angle)
rotate a sprite around its centre, angle in 256ths of a turn, clockwise
void stretch_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, int w, int h)
draw a sprite scaled to w x h
void putpixel(BITMAP *bmp, int x, int y, int color)
write a pixel
void set_color_depth(int depth)
set the color depth, only 32 is honoured
void clear_to_color(BITMAP *bmp, int color)
clear to a colour
void rectfill(BITMAP *bmp, int x1, int y1, int x2, int y2, int color)
draw a filled rectangle
void triangle(BITMAP *bmp, int x1, int y1, int x2, int y2, int x3, int y3, int color)
draw a filled triangle
void pivot_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, int cx, int cy, fixed angle)
rotate a sprite around (cx,cy) in the sprite, drawn at (x,y)
void rest(unsigned int ms)
sleep ms milliseconds
void circlefill(BITMAP *bmp, int x, int y, int radius, int color)
draw a filled circle
void draw_sprite_v_flip(BITMAP *bmp, BITMAP *sprite, int x, int y)
draw a sprite mirrored vertically
void draw_trans_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y)
draw a sprite with the current blender (set_alpha_blender / set_trans_blender)
int getpixel(BITMAP *bmp, int x, int y)
read a pixel, -1 outside the bitmap
void allegro_gl_flip(void)
show the screen bitmap
void rect(BITMAP *bmp, int x1, int y1, int x2, int y2, int color)
draw a rectangle outline
Allegro 4 bitmap.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:104
#define LOG_NULL
no log output
Definition n_log.h:70
#define LOG_INFO
informational
Definition n_log.h:106
Allegro 4 compatibility layer on top of Allegro 5.
Common headers and low-level functions & define.
Generic log system.