6 ex__atlas_8c
gullradriel edited this page 2026-10-09 00:55:18 +02:00

examples/ex_atlas.c

Functions

Name
unsigned char * make_png(int w, int h, size_t * out_len)
int main(void )
void expect_true(const char * label, int cond)
void expect_eqf(const char * label, float got, float want)

Attributes

Name
int failures

Defines

Name
ALLEGRO_UNSTABLE

Functions Documentation

function make_png

static unsigned char * make_png(
    int w,
    int h,
    size_t * out_len
)

function main

int main(
    void 
)

function expect_true

static void expect_true(
    const char * label,
    int cond
)

function expect_eqf

static void expect_eqf(
    const char * label,
    float got,
    float want
)

Attributes Documentation

variable failures

static int failures = 0;

Macros Documentation

define ALLEGRO_UNSTABLE

#define ALLEGRO_UNSTABLE 1

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.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#define ALLEGRO_UNSTABLE 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_atlas.h"

#include "allegro5/allegro_image.h"
#include "allegro5/allegro_memfile.h"

static int failures = 0;

static void expect_true(const char* label, int cond) {
    if (!cond) {
        n_log(LOG_ERR, "%s: condition false", label);
        failures++;
    } else {
        n_log(LOG_INFO, "%s: OK", label);
    }
}

static void expect_eqf(const char* label, float got, float want) {
    float d = got - want;
    if (d < 0.0f) {
        d = -d;
    }
    if (d > 0.01f) {
        n_log(LOG_ERR, "%s: got %f want %f", label, got, want);
        failures++;
    } else {
        n_log(LOG_INFO, "%s: OK", label);
    }
}

/* encode a cols*cell x rows*cell test image to PNG in memory */
static unsigned char* make_png(int w, int h, size_t* out_len) {
    ALLEGRO_BITMAP* bmp = NULL;
    ALLEGRO_FILE* mf = NULL;
    unsigned char* buf = NULL;
    size_t cap = 256 * 1024;
    int64_t n = 0;

    al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
    bmp = al_create_bitmap(w, h);
    if (!bmp) {
        return NULL;
    }
    al_set_target_bitmap(bmp);
    al_clear_to_color(al_map_rgb(10, 20, 30));
    al_set_target_bitmap(NULL);

    buf = malloc(cap);
    if (!buf) {
        al_destroy_bitmap(bmp);
        return NULL;
    }
    mf = al_open_memfile(buf, (int64_t)cap, "w");
    if (!mf || !al_save_bitmap_f(mf, ".png", bmp)) {
        if (mf) {
            al_fclose(mf);
        }
        al_destroy_bitmap(bmp);
        free(buf);
        return NULL;
    }
    n = al_ftell(mf);
    al_fclose(mf);
    al_destroy_bitmap(bmp);
    if (n <= 0) {
        free(buf);
        return NULL;
    }
    (*out_len) = (size_t)n;
    return buf;
}

int main(void) {
    N_ATLAS* atlas = NULL;
    unsigned char* png = NULL;
    size_t png_len = 0;
    float sx = 0.0f, sy = 0.0f, sw = 0.0f, sh = 0.0f;

    set_log_level(LOG_ERR);

    if (!al_init()) {
        n_log(LOG_ERR, "could not init allegro");
        return 1;
    }
    al_init_image_addon();

    png = make_png(64, 32, &png_len); /* 4 cols x 2 rows of 16px cells */
    expect_true("png encoded", png != NULL && png_len > 0);
    if (!png) {
        return 1;
    }

    atlas = new_n_atlas(2);
    expect_true("atlas created", atlas != NULL);
    expect_true("page 0 not ready before load", !n_atlas_page_ready(atlas, 0));

    expect_true("set_page", n_atlas_set_page(atlas, 0, png, png_len, ".png", 4, 2) == TRUE);
    expect_true("page 0 ready", n_atlas_page_ready(atlas, 0) == TRUE);
    expect_true("page 1 still empty", !n_atlas_page_ready(atlas, 1));

    /* cell geometry is derived from the bitmap and grid: 64/4 = 16 wide */
    expect_true("cell 0 rect", n_atlas_cell_rect(atlas, 0, 0, &sx, &sy, &sw, &sh) == TRUE);
    expect_eqf("cell 0 x", sx, 0.0f);
    expect_eqf("cell 0 y", sy, 0.0f);
    expect_eqf("cell w", sw, 16.0f);
    expect_eqf("cell h", sh, 16.0f);

    /* index 5 = col 1, row 1 (left to right, then top to bottom) */
    expect_true("cell 5 rect", n_atlas_cell_rect(atlas, 0, 5, &sx, &sy, &sw, &sh) == TRUE);
    expect_eqf("cell 5 x", sx, 16.0f);
    expect_eqf("cell 5 y", sy, 16.0f);

    /* last valid cell of a 4x2 grid is index 7 */
    expect_true("cell 7 exists", n_atlas_cell_rect(atlas, 0, 7, NULL, NULL, NULL, NULL) == TRUE);
    expect_true("cell 8 past the grid is refused", n_atlas_cell_rect(atlas, 0, 8, NULL, NULL, NULL, NULL) == FALSE);
    expect_true("unloaded page is refused", n_atlas_cell_rect(atlas, 1, 0, NULL, NULL, NULL, NULL) == FALSE);
    expect_true("out of range page is refused", n_atlas_cell_rect(atlas, 99, 0, NULL, NULL, NULL, NULL) == FALSE);

    /* bad arguments are refused, and leave the loaded page alone */
    expect_true("zero cols refused", n_atlas_set_page(atlas, 0, png, png_len, ".png", 0, 2) == FALSE);
    expect_true("out of range page refused", n_atlas_set_page(atlas, 99, png, png_len, ".png", 4, 2) == FALSE);
    expect_true("undecodable bytes refused", n_atlas_set_page(atlas, 1, "not a png", 9, ".png", 4, 2) == FALSE);
    expect_true("page 0 survived the refusals", n_atlas_page_ready(atlas, 0) == TRUE);

    /* replacing a page frees the old bitmap: ASan catches a leak here */
    expect_true("replace page 0", n_atlas_set_page(atlas, 0, png, png_len, ".png", 2, 1) == TRUE);
    expect_true("regridded cell w", n_atlas_cell_rect(atlas, 0, 0, NULL, NULL, &sw, NULL) == TRUE);
    expect_eqf("regridded cell w = 32", sw, 32.0f);

    expect_true("max bitmap size sane", n_atlas_max_bitmap_size() >= 2048);

    n_atlas_destroy(&atlas);
    expect_true("destroy nulls the handle", atlas == NULL);
    n_atlas_destroy(&atlas); /* NULL-safe second call */

    free(png);

    if (failures) {
        n_log(LOG_ERR, "ex_atlas: %d failure(s)", failures);
        return 1;
    }
    printf("ex_atlas: all checks passed\n");
    return 0;
}