Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_allegro_4.c

Allegro 4 compatibility layer: a scene drawn with Allegro 4 calls.

Allegro 4 compatibility layer: a scene drawn with Allegro 4 calls Draws primitives, masked / flipped / rotated / stretched sprites, alpha and constant opacity blending, text and direct pixel access, all through the Allegro 4 API of n_allegro_4.h, and checks a few pixels back. Escape quits. -s renders one frame, saves it to ex_allegro_4.png and exits, which is how the layer is checked without anyone watching. It needs a display: without one it skips with exit code 0, run it under xvfb-run to exercise it headless.

Author
Castagnier Mickael
Version
1.0
Date
08/10/2026
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include "nilorea/n_log.h"
#include <getopt.h>
static int screenshot_mode = 0;
static volatile int close_asked = 0;
static void close_cb(void) {
}
void usage(void) {
fprintf(stderr,
" -v version\n"
" -V log level: LOG_INFO, LOG_NOTICE, LOG_ERR, LOG_DEBUG\n"
" -s render one frame to ex_allegro_4.png and exit\n"
" -h help\n");
}
void process_args(int argc, char** argv) {
while ((getoptret = getopt(argc, argv, "hvsV:")) != EOF) {
switch (getoptret) {
case 'v':
fprintf(stderr, "Date de compilation : %s a %s.\n", __DATE__, __TIME__);
exit(0);
case 'V':
if (!strncmp("LOG_NULL", optarg, 8)) {
} else if (!strncmp("LOG_NOTICE", optarg, 10)) {
} else if (!strncmp("LOG_INFO", optarg, 8)) {
} else if (!strncmp("LOG_ERR", optarg, 7)) {
} else if (!strncmp("LOG_DEBUG", optarg, 9)) {
} else {
fprintf(stderr, "%s is not a valid log level\n", optarg);
exit(1);
}
break;
case 's':
break;
default:
case 'h':
usage();
exit(1);
}
}
}
/* A 64x64 sprite: a ball with an arrow on a mask colour background. */
static BITMAP* make_sprite(void) {
BITMAP* spr = create_bitmap(64, 64);
if (!spr) return NULL;
circlefill(spr, 32, 32, 26, makecol(240, 200, 40));
circle(spr, 32, 32, 26, makecol(120, 80, 0));
triangle(spr, 32, 10, 22, 30, 42, 30, makecol(200, 30, 30));
rectfill(spr, 29, 28, 35, 52, makecol(200, 30, 30));
return spr;
}
/* Check pixel (x,y) of bmp against an expected rgb, logging a mismatch. */
static int expect(BITMAP* bmp, int x, int y, int r, int g, int b, const char* what) {
int c = getpixel(bmp, x, y);
if (getr(c) != r || getg(c) != g || getb(c) != b) {
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);
return FALSE;
}
return TRUE;
}
static void draw_scene(BITMAP* buf, BITMAP* spr, double t) {
clear_to_color(buf, makecol(20, 30, 60));
/* primitives */
rectfill(buf, 20, 20, 219, 119, makecol(60, 120, 60));
rect(buf, 20, 20, 219, 119, makecol(255, 255, 255));
line(buf, 20, 20, 219, 119, makecol(255, 255, 0));
circle(buf, 320, 70, 50, makecol(0, 200, 255));
ellipsefill(buf, 480, 70, 60, 30, makecol(150, 60, 200));
/* sprites */
draw_sprite(buf, spr, 20, 150);
draw_sprite_h_flip(buf, spr, 100, 150);
draw_sprite_v_flip(buf, spr, 180, 150);
stretch_sprite(buf, spr, 260, 150, 128, 64);
rotate_sprite(buf, spr, 420, 150, ftofix(t * 64.0));
pivot_sprite(buf, spr, 560, 182, 32, 32, itofix(64));
/* blending: alpha of the colour, then a constant opacity */
rectfill(buf, 20, 240, 219, 319, makeacol(255, 0, 0, 128));
rectfill(buf, 120, 280, 319, 359, makeacol(0, 0, 255, 128));
set_trans_blender(0, 0, 0, 96);
draw_trans_sprite(buf, spr, 360, 260);
/* direct pixel access, PMASK style */
for (int y = 0; y < 40; y++) {
uintptr_t addr = bmp_write_line(buf, 260 + y);
for (int x = 0; x < 40; x++) bmp_write32(addr + (uintptr_t)(480 + x) * 4, makecol(x * 6, y * 6, 128));
}
for (int x = 0; x < 40; x++) putpixel(buf, 530 + x, 280, makecol(255, 255, 255));
/* text */
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);
textprintf_centre_ex(buf, font, buf->w / 2, 430, makecol(0, 0, 0), makecol(255, 220, 0), " centred, with a background ");
textout_right_ex(buf, font, "right aligned", buf->w - 20, 460, makecol(180, 180, 255), -1);
}
int main(int argc, char** argv) {
process_args(argc, argv);
if (allegro_init() != 0) {
n_log(LOG_ERR, "allegro_init: %s", allegro_error);
return 1;
}
if (set_gfx_mode(GFX_OPENGL_WINDOWED, 640, 480, 0, 0) != 0) {
/* no display (headless CI): nothing to check, skip */
n_log(LOG_NOTICE, "set_gfx_mode: %s, skipping", allegro_error);
return 0;
}
set_window_title("ex_allegro_4");
BITMAP* spr = make_sprite();
if (!spr || !buf) {
n_log(LOG_ERR, "could not create the bitmaps");
return 1;
}
int ok = TRUE;
/* the sprite keeps the mask colour as a plain colour */
if (getpixel(spr, 0, 0) != bitmap_mask_color(spr)) {
n_log(LOG_ERR, "mask colour not kept: %08x", (unsigned)getpixel(spr, 0, 0));
ok = FALSE;
}
if (fixtoi(itofix(12)) != 12 || fixtoi(ftofix(2.5)) != 3) {
n_log(LOG_ERR, "fixed point conversions are off");
ok = FALSE;
}
double t = 0.0;
do {
draw_scene(buf, spr, t);
blit(buf, screen, 0, 0, 0, 0, buf->w, buf->h);
t += 0.02;
rest(10);
} while (!screenshot_mode && !key[KEY_ESC] && !close_asked);
/* masked draws leave the background, solid ones overwrite it */
ok = expect(buf, 21, 151, 20, 30, 60, "draw_sprite mask") && ok;
ok = expect(buf, 32, 182, 240, 200, 40, "draw_sprite body") && ok;
ok = expect(buf, 100, 30, 60, 120, 60, "rectfill") && ok;
ok = expect(buf, 485, 265, 30, 30, 128, "bmp_write32") && ok;
ok = expect(buf, 540, 280, 255, 255, 255, "putpixel") && ok;
/* 50% red over the background: (255+20)/2, (0+30)/2, (0+60)/2 */
int c = getpixel(buf, 50, 250);
if (abs(getr(c) - 137) > 2 || abs(getg(c) - 15) > 2 || abs(getb(c) - 30) > 2) {
n_log(LOG_ERR, "alpha blend: %d,%d,%d, expected about 137,15,30", getr(c), getg(c), getb(c));
ok = FALSE;
}
/* the fade trick: Allegro 4 takes at least one step off per frame, so a
* black overlay at opacity 1 ends on black, and opacity 64 takes
* ceil(60 * 65 / 256) = 16 off 60 in one go */
BITMAP* fade = create_bitmap(8, 8);
if (fade) {
clear_to_color(fade, makecol(60, 60, 60));
rectfill(fade, 0, 0, 7, 7, makeacol(0, 0, 0, 64));
ok = expect(fade, 2, 2, 44, 44, 44, "fade step at opacity 64") && ok;
for (int i = 0; i < 255; i++) rectfill(fade, 0, 0, 7, 7, makeacol(0, 0, 0, 1));
ok = expect(fade, 2, 2, 0, 0, 0, "fade to black at opacity 1") && ok;
}
if (screenshot_mode && save_bitmap("ex_allegro_4.png", buf, NULL) != 0) {
n_log(LOG_ERR, "could not save ex_allegro_4.png");
ok = FALSE;
}
n_log(LOG_NOTICE, "ex_allegro_4: %s", ok ? "all checks passed" : "some checks FAILED");
return ok ? 0 : 1;
}
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.