3 ex__allegro__4_8c
gullradriel edited this page 2026-10-09 00:55:18 +02:00

examples/ex_allegro_4.c

Functions

Name
void usage(void )
void process_args(int argc, char ** argv)
BITMAP * make_sprite(void )
int main(int argc, char ** argv)
int expect(BITMAP * bmp, int x, int y, int r, int g, int b, const char * what)
void draw_scene(BITMAP * buf, BITMAP * spr, double t)
void close_cb(void )

Attributes

Name
int screenshot_mode
volatile int close_asked

Functions Documentation

function usage

void usage(
    void 
)

function process_args

void process_args(
    int argc,
    char ** argv
)

function make_sprite

static BITMAP * make_sprite(
    void 
)

function main

int main(
    int argc,
    char ** argv
)

function expect

static int expect(
    BITMAP * bmp,
    int x,
    int y,
    int r,
    int g,
    int b,
    const char * what
)

function draw_scene

static void draw_scene(
    BITMAP * buf,
    BITMAP * spr,
    double t
)

function close_cb

static void close_cb(
    void 
)

Attributes Documentation

variable screenshot_mode

static int screenshot_mode = 0;

variable close_asked

static volatile int close_asked = 0;

Source code

/*
 * 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_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_allegro_4.h"

#include <getopt.h>

static int screenshot_mode = 0;
static volatile int close_asked = 0;

static void close_cb(void) {
    close_asked = 1;
}

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) {
    int getoptret = 0, log_level = LOG_ERR;
    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)) {
                    log_level = LOG_NULL;
                } else if (!strncmp("LOG_NOTICE", optarg, 10)) {
                    log_level = LOG_NOTICE;
                } else if (!strncmp("LOG_INFO", optarg, 8)) {
                    log_level = LOG_INFO;
                } else if (!strncmp("LOG_ERR", optarg, 7)) {
                    log_level = LOG_ERR;
                } else if (!strncmp("LOG_DEBUG", optarg, 9)) {
                    log_level = LOG_DEBUG;
                } else {
                    fprintf(stderr, "%s is not a valid log level\n", optarg);
                    exit(1);
                }
                break;
            case 's':
                screenshot_mode = 1;
                break;
            default:
            case 'h':
                usage();
                exit(1);
        }
    }
    set_log_level(log_level);
}

/* 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;
    clear_to_color(spr, bitmap_mask_color(spr));
    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) {
    drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
    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 */
    drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
    set_alpha_blender();
    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);
    drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);

    /* 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));
        bmp_unwrite_line(buf);
    }
    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;
    }
    install_keyboard();
    install_mouse();
    install_timer();
    set_color_depth(32);
    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);
        allegro_exit();
        return 0;
    }
    set_window_title("ex_allegro_4");
    set_close_button_callback(close_cb);

    BITMAP* spr = make_sprite();
    BITMAP* buf = create_bitmap(SCREEN_W, SCREEN_H);
    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);
        allegro_gl_flip();
        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));
        drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
        set_alpha_blender();
        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));
        drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
        ok = expect(fade, 2, 2, 0, 0, 0, "fade to black at opacity 1") && ok;
        destroy_bitmap(fade);
    }

    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;
    }

    destroy_bitmap(buf);
    destroy_bitmap(spr);
    allegro_exit();
    n_log(LOG_NOTICE, "ex_allegro_4: %s", ok ? "all checks passed" : "some checks FAILED");
    return ok ? 0 : 1;
}