Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_atlas.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 * SPDX-License-Identifier: Apache-2.0
18 */
19
37
38#define ALLEGRO_UNSTABLE 1
39
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43
44#include "nilorea/n_common.h"
45#include "nilorea/n_log.h"
46#include "nilorea/n_atlas.h"
47
48#include "allegro5/allegro_image.h"
49#include "allegro5/allegro_memfile.h"
50
51static int failures = 0;
52
53static void expect_true(const char* label, int cond) {
54 if (!cond) {
55 n_log(LOG_ERR, "%s: condition false", label);
56 failures++;
57 } else {
58 n_log(LOG_INFO, "%s: OK", label);
59 }
60}
61
62static void expect_eqf(const char* label, float got, float want) {
63 float d = got - want;
64 if (d < 0.0f) {
65 d = -d;
66 }
67 if (d > 0.01f) {
68 n_log(LOG_ERR, "%s: got %f want %f", label, got, want);
69 failures++;
70 } else {
71 n_log(LOG_INFO, "%s: OK", label);
72 }
73}
74
75/* encode a cols*cell x rows*cell test image to PNG in memory */
76static unsigned char* make_png(int w, int h, size_t* out_len) {
77 ALLEGRO_BITMAP* bmp = NULL;
78 ALLEGRO_FILE* mf = NULL;
79 unsigned char* buf = NULL;
80 size_t cap = 256 * 1024;
81 int64_t n = 0;
82
83 al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
84 bmp = al_create_bitmap(w, h);
85 if (!bmp) {
86 return NULL;
87 }
88 al_set_target_bitmap(bmp);
89 al_clear_to_color(al_map_rgb(10, 20, 30));
90 al_set_target_bitmap(NULL);
91
92 buf = malloc(cap);
93 if (!buf) {
94 al_destroy_bitmap(bmp);
95 return NULL;
96 }
97 mf = al_open_memfile(buf, (int64_t)cap, "w");
98 if (!mf || !al_save_bitmap_f(mf, ".png", bmp)) {
99 if (mf) {
100 al_fclose(mf);
101 }
102 al_destroy_bitmap(bmp);
103 free(buf);
104 return NULL;
105 }
106 n = al_ftell(mf);
107 al_fclose(mf);
108 al_destroy_bitmap(bmp);
109 if (n <= 0) {
110 free(buf);
111 return NULL;
112 }
113 (*out_len) = (size_t)n;
114 return buf;
115}
116
117int main(void) {
118 N_ATLAS* atlas = NULL;
119 unsigned char* png = NULL;
120 size_t png_len = 0;
121 float sx = 0.0f, sy = 0.0f, sw = 0.0f, sh = 0.0f;
122
124
125 if (!al_init()) {
126 n_log(LOG_ERR, "could not init allegro");
127 return 1;
128 }
129 al_init_image_addon();
130
131 png = make_png(64, 32, &png_len); /* 4 cols x 2 rows of 16px cells */
132 expect_true("png encoded", png != NULL && png_len > 0);
133 if (!png) {
134 return 1;
135 }
136
137 atlas = new_n_atlas(2);
138 expect_true("atlas created", atlas != NULL);
139 expect_true("page 0 not ready before load", !n_atlas_page_ready(atlas, 0));
140
141 expect_true("set_page", n_atlas_set_page(atlas, 0, png, png_len, ".png", 4, 2) == TRUE);
142 expect_true("page 0 ready", n_atlas_page_ready(atlas, 0) == TRUE);
143 expect_true("page 1 still empty", !n_atlas_page_ready(atlas, 1));
144
145 /* cell geometry is derived from the bitmap and grid: 64/4 = 16 wide */
146 expect_true("cell 0 rect", n_atlas_cell_rect(atlas, 0, 0, &sx, &sy, &sw, &sh) == TRUE);
147 expect_eqf("cell 0 x", sx, 0.0f);
148 expect_eqf("cell 0 y", sy, 0.0f);
149 expect_eqf("cell w", sw, 16.0f);
150 expect_eqf("cell h", sh, 16.0f);
151
152 /* index 5 = col 1, row 1 (left to right, then top to bottom) */
153 expect_true("cell 5 rect", n_atlas_cell_rect(atlas, 0, 5, &sx, &sy, &sw, &sh) == TRUE);
154 expect_eqf("cell 5 x", sx, 16.0f);
155 expect_eqf("cell 5 y", sy, 16.0f);
156
157 /* last valid cell of a 4x2 grid is index 7 */
158 expect_true("cell 7 exists", n_atlas_cell_rect(atlas, 0, 7, NULL, NULL, NULL, NULL) == TRUE);
159 expect_true("cell 8 past the grid is refused", n_atlas_cell_rect(atlas, 0, 8, NULL, NULL, NULL, NULL) == FALSE);
160 expect_true("unloaded page is refused", n_atlas_cell_rect(atlas, 1, 0, NULL, NULL, NULL, NULL) == FALSE);
161 expect_true("out of range page is refused", n_atlas_cell_rect(atlas, 99, 0, NULL, NULL, NULL, NULL) == FALSE);
162
163 /* bad arguments are refused, and leave the loaded page alone */
164 expect_true("zero cols refused", n_atlas_set_page(atlas, 0, png, png_len, ".png", 0, 2) == FALSE);
165 expect_true("out of range page refused", n_atlas_set_page(atlas, 99, png, png_len, ".png", 4, 2) == FALSE);
166 expect_true("undecodable bytes refused", n_atlas_set_page(atlas, 1, "not a png", 9, ".png", 4, 2) == FALSE);
167 expect_true("page 0 survived the refusals", n_atlas_page_ready(atlas, 0) == TRUE);
168
169 /* replacing a page frees the old bitmap: ASan catches a leak here */
170 expect_true("replace page 0", n_atlas_set_page(atlas, 0, png, png_len, ".png", 2, 1) == TRUE);
171 expect_true("regridded cell w", n_atlas_cell_rect(atlas, 0, 0, NULL, NULL, &sw, NULL) == TRUE);
172 expect_eqf("regridded cell w = 32", sw, 32.0f);
173
174 expect_true("max bitmap size sane", n_atlas_max_bitmap_size() >= 2048);
175
176 n_atlas_destroy(&atlas);
177 expect_true("destroy nulls the handle", atlas == NULL);
178 n_atlas_destroy(&atlas); /* NULL-safe second call */
179
180 free(png);
181
182 if (failures) {
183 n_log(LOG_ERR, "ex_atlas: %d failure(s)", failures);
184 return 1;
185 }
186 printf("ex_atlas: all checks passed\n");
187 return 0;
188}
static int failures
Definition ex_atlas.c:51
static void expect_eqf(const char *label, float got, float want)
Definition ex_atlas.c:62
int main(void)
Definition ex_atlas.c:117
static unsigned char * make_png(int w, int h, size_t *out_len)
Definition ex_atlas.c:76
static void expect_true(const char *label, int cond)
Definition ex_atlas.c:53
int n_atlas_max_bitmap_size(void)
get the largest bitmap dimension this display can hold
Definition n_atlas.c:43
int n_atlas_set_page(N_ATLAS *atlas, size_t page, const void *bytes, size_t len, const char *ext, uint16_t cols, uint16_t rows)
decode bytes into one atlas page
Definition n_atlas.c:112
void n_atlas_destroy(N_ATLAS **atlas)
destroy an atlas and every bitmap it owns
Definition n_atlas.c:75
int n_atlas_page_ready(const N_ATLAS *atlas, size_t page)
test whether a page holds a usable bitmap
Definition n_atlas.c:160
int n_atlas_cell_rect(const N_ATLAS *atlas, size_t page, size_t index, float *sx, float *sy, float *sw, float *sh)
compute the source rectangle of one cell
Definition n_atlas.c:183
N_ATLAS * new_n_atlas(size_t npages)
allocate an atlas holding npages empty pages
Definition n_atlas.c:54
a set of atlas pages addressed as (page, cell index)
Definition n_atlas.h:55
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#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_INFO
informational
Definition n_log.h:106
Multi-page sprite atlas: random-access cell blitting from a grid.
Common headers and low-level functions & define.
Generic log system.