add parabole

This commit is contained in:
Frater 2026-05-15 12:57:47 +02:00
commit a89bea929a
6 changed files with 447 additions and 32 deletions

View file

@ -2,9 +2,12 @@
int main(void) {
Universe *uni;
Object3D *sph, *cyl, *tmp_obj;
Object3D *bus, *dish, *rtg_boom, *rtg1, *rtg2, *rtg3;
Object3D *mag_boom, *sci_boom, *cam_box, *record, *feed_boom, *sub_dish;
Object3D *tank, *pws1, *pws2, *inst1, *inst2;
Camera cam;
Matrix3 rotX, rotY, orbit;
Image *bg_img = NULL;
int special=0;
int anglex, angley, key, render, o;
fixed bx, by, bz, distance;
@ -20,36 +23,95 @@ int main(void) {
if (!uni) return 1;
uni->num_verts = uni->num_faces = uni->num_objects = 0;
/* --- Ajout des objets : géométrie locale en (0,0,0) --- */
sph = add_sphere(uni, 0, 0, 0, int_to_f(30), 8, COLOR4, 0);
if (!sph) { free(uni); return 1; }
set_object_pos(sph,int_to_f(70),0,0);
//set_object_color(COLOR4);
/* --- Construction de la sonde Voyager --- */
cyl = add_cylinder(uni, 0, 0, 0, int_to_f(20), int_to_f(60), 12, COLOR2, 0);
if (!cyl) { free(uni); return 1; }
set_object_pos(cyl,int_to_f(-70),0,0);
//set_object_color(COLOR2);
/* 1. Bus principal (décagone blanc) */
bus = add_cylinder(uni, 0, 0, 0, int_to_f(12), int_to_f(10), 10, COLOR7, 0);
set_object_pos(bus, 0, 0, 0);
//tmp_obj = add_cone(uni, 0, 0, 0, int_to_f(20), int_to_f(60), 12, COLOR1, 0);
tmp_obj = add_torus(uni, 0, 0, 0, int_to_f(20), int_to_f(10), 12, 12, COLOR1, 0);
/* 1b. Réservoir d'hydrazine central (sphère dépassant sous le bus) */
tank = add_sphere(uni, 0, 0, 0, int_to_f(6), 8, COLOR7, 0);
set_object_pos(tank, 0, int_to_f(-3), 0);
/* 2a. Antenne parabolique (Paraboloïde avec courbure réaliste) */
dish = add_paraboloid(uni, 0, 0, 0, int_to_f(25), int_to_f(8), 24, 4, COLOR7, 0);
/* Pointant déjà vers le haut (+Y), on abaisse simplement sa base pour se reposer sur le bus */
set_object_pos(dish, 0, int_to_f(9), 0);
// tmp_obj = add_math_surface(uni, 0, 0, 0, int_to_f(120), int_to_f(120), 30, 30, COLOR2, 0);
// set_object_pos(tmp_obj, 0, int_to_f(-40), 0);
/* 2b. Mât central de l'antenne (treillis triangulaire) */
feed_boom = add_cylinder(uni, 0, 0, 0, int_to_f(1)/2, int_to_f(10), 3, COLOR7, 1);
set_object_pos(feed_boom, 0, int_to_f(10), 0);
/* 2c. Sous-réflecteur de l'antenne (petit cône inversé) */
sub_dish = add_cone(uni, 0, 0, 0, int_to_f(3), int_to_f(2), 12, COLOR7, 0);
bake_object_rotation(uni, sub_dish, 180, 0, 0);
set_object_pos(sub_dish, 0, int_to_f(16), 0);
/* Pour ajouter un troisième objet : */
/*
Object3D *sph2 = add_sphere(uni, 0, 0, 0, int_to_f(15), 6, COLOR6, 0);
sph2->base_pos.x = 0; sph2->base_pos.y = int_to_f(60); sph2->base_pos.z = 0;
sph2->color = COLOR6;
*/
/* 3. Mât du RTG (Générateur thermoélectrique, structure en treillis carrée) */
rtg_boom = add_cylinder(uni, 0, 0, 0, int_to_f(2), int_to_f(40), 4, COLOR11, 1);
bake_object_rotation(uni, rtg_boom, 0, 0, 90); /* Couché sur l'axe X */
set_object_pos(rtg_boom, int_to_f(25), int_to_f(-2), 0);
/* 4. RTG (Générateurs thermoélectriques, 3 segments en série) */
rtg1 = add_cylinder(uni, 0, 0, 0, int_to_f(4), int_to_f(5), 8, COLOR11, 0);
bake_object_rotation(uni, rtg1, 0, 0, 90);
set_object_pos(rtg1, int_to_f(48), int_to_f(-2), 0);
rtg2 = add_cylinder(uni, 0, 0, 0, int_to_f(4), int_to_f(5), 8, COLOR11, 0);
bake_object_rotation(uni, rtg2, 0, 0, 90);
set_object_pos(rtg2, int_to_f(54), int_to_f(-2), 0);
rtg3 = add_cylinder(uni, 0, 0, 0, int_to_f(4), int_to_f(5), 8, COLOR11, 0);
bake_object_rotation(uni, rtg3, 0, 0, 90);
set_object_pos(rtg3, int_to_f(60), int_to_f(-2), 0);
/* 5. Mât du magnétomètre (Très long, structure en treillis triangulaire) */
mag_boom = add_cylinder(uni, 0, 0, 0, int_to_f(1), int_to_f(70), 3, COLOR11, 1);
bake_object_rotation(uni, mag_boom, 0, 0, 90);
set_object_pos(mag_boom, int_to_f(-40), int_to_f(-2), 0);
/* 6. Mât de la plateforme scientifique (vers l'avant, axe Z, treillis carrée) */
sci_boom = add_cylinder(uni, 0, 0, 0, int_to_f(2), int_to_f(30), 4, COLOR11, 1);
bake_object_rotation(uni, sci_boom, 90, 0, 0); /* Couché sur l'axe Z */
set_object_pos(sci_boom, 0, int_to_f(-2), int_to_f(-20));
/* 7. Plateforme et caméras (cube gris) */
cam_box = add_box(uni, 0, 0, 0, int_to_f(6), int_to_f(6), int_to_f(6), COLOR11, 0);
set_object_pos(cam_box, 0, int_to_f(-2), int_to_f(-35));
/* 8. Le célèbre Golden Record ! (Petit cylindre jaune/or collé sur le flanc) */
record = add_cylinder(uni, 0, 0, 0, int_to_f(3), int_to_f(1), 10, COLOR4, 0);
bake_object_rotation(uni, record, 0, 0, 90);
set_object_pos(record, int_to_f(12), int_to_f(0), int_to_f(5));
/* 9. Antennes PWS (Plasma Wave Subsystem) - Longs tubes filaires en V */
pws1 = add_cylinder(uni, 0, 0, 0, int_to_f(1)/2, int_to_f(60), 3, COLOR11, 1);
bake_object_rotation(uni, pws1, 0, 0, 90); /* 1. On le couche sur l'axe X */
bake_object_rotation(uni, pws1, 0, 45, 0); /* 2. On l'écarte en V sur le plan XZ */
set_object_pos(pws1, int_to_f(-20), int_to_f(-5), int_to_f(20));
pws2 = add_cylinder(uni, 0, 0, 0, int_to_f(1)/2, int_to_f(60), 3, COLOR11, 1);
bake_object_rotation(uni, pws2, 0, 0, 90); /* 1. On le couche sur l'axe X */
bake_object_rotation(uni, pws2, 0, -45, 0);/* 2. On l'écarte en V sur le plan XZ */
set_object_pos(pws2, int_to_f(-20), int_to_f(-5), int_to_f(-20));
/* 10. Instruments optiques sur la plateforme scientifique (Télescopes/Caméras) */
inst1 = add_cylinder(uni, 0, 0, 0, int_to_f(2), int_to_f(8), 6, COLOR7, 0);
bake_object_rotation(uni, inst1, 90, 0, 0);
set_object_pos(inst1, int_to_f(-2), int_to_f(1), int_to_f(-38));
inst2 = add_cone(uni, 0, 0, 0, int_to_f(2), int_to_f(6), 8, COLOR7, 0);
bake_object_rotation(uni, inst2, 90, 0, 0);
set_object_pos(inst2, int_to_f(2), int_to_f(1), int_to_f(-38));
cam.pos.x = cam.pos.y = cam.pos.z = 0;
if (init_vga() != 0) { free(uni); return 1; }
/* Optionnel : charger un fond (BMP 8-bit de 320x200 couleurs indexees) */
/* S'assurer que le fichier est bien généré avec les couleurs de votre palette moteur ! */
/* bg_img = load_bmp8("stars.bmp", NULL); */
while (key != 27) {
if (kbhit()) {
special = 0;
@ -104,11 +166,19 @@ int main(void) {
obj->pos.z = distance + f_mul(bx, orbit.m[0][2]) + f_mul(by, orbit.m[1][2]) + f_mul(bz, orbit.m[2][2]);
}
clear_buffers(0);
if (bg_img) {
/* Dessiner le fond (qui écrase tout) et vider juste le Z-Buffer */
blit_image(bg_img, 0, 0);
clear_zbuffer();
} else {
clear_buffers(0);
}
render_universe(uni, &cam, render);
flip();
}
if (bg_img) free_image(bg_img);
close_vga();
free(uni);
return 0;

View file

@ -56,6 +56,12 @@ typedef struct { fixed x, y, z; } Vector3;
typedef struct { int a, b, c; uint8_t force_wire; } Face;
typedef struct { int x, y; fixed z; fixed intensity; } Point2D;
typedef struct {
int width;
int height;
uint8_t *data;
} Image;
/* Un objet dans l'univers : tranche du pool partagé + transform */
typedef struct {
int vert_start, num_verts; /* indices dans Universe.verts/v_normals */
@ -117,6 +123,7 @@ Object3D* add_sphere (Universe *uni, fixed x, fixed y, fixed z, fixed r,
Object3D* add_cylinder (Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire);
Object3D* add_cone (Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, uint8_t col, uint8_t wire);
Object3D* add_plane (Universe *uni, fixed x, fixed y, fixed z, fixed w, fixed d, int seg_x, int seg_z, uint8_t col, uint8_t wire);
Object3D* add_paraboloid(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, int rings, uint8_t col, uint8_t wire);
Object3D* add_torus (Universe *uni, fixed x, fixed y, fixed z, fixed r_main, fixed r_tube, int seg_main, int seg_tube, uint8_t col, uint8_t wire);
Object3D* add_math_surface(Universe *uni, fixed x, fixed y, fixed z,
fixed w, fixed d, int seg_x, int seg_z,
@ -126,6 +133,10 @@ void render_universe(Universe *uni, Camera *cam, int mode);
int init_vga(void);
void clear_buffers(uint8_t color);
void clear_zbuffer(void);
void blit_image(Image *img, int x, int y);
void blit_image_transparent(Image *img, int x, int y, uint8_t color_key);
void apply_256_palette(RGB *pal);
void flip(void);
void close_vga(void);
@ -136,4 +147,8 @@ void save_mesh(Mesh *m, const char *fn);
Mesh *load_mesh(const char *fname);
void free_mesh(Mesh *m);
Image *create_image(int width, int height);
void free_image(Image *img);
Image *load_bmp8(const char *filename, RGB *pal_out);
#endif

View file

@ -61,6 +61,7 @@ void mat3_rotate_z(Matrix3 *m, int angle) {
}
/* Fait pivoter la géométrie locale de l'objet de façon permanente */
/*
void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az) {
Matrix3 rx, ry, rz, tmp, rot;
Vector3 *v, *n;
@ -70,20 +71,20 @@ void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az)
if (!obj || !uni) return;
/* Composition de la matrice de rotation */
/* Composition de la matrice de rotation * /
mat3_rotate_x(&rx, ax);
mat3_rotate_y(&ry, ay);
mat3_rotate_z(&rz, az);
mat3_mul(&tmp, &rx, &ry);
mat3_mul(&rot, &tmp, &rz);
/* Application à tous les sommets et normales de l'objet */
/* Application à tous les sommets et normales de l'objet * /
for (i = 0; i < obj->num_verts; i++) {
vi = obj->vert_start + i;
v = &uni->verts[vi];
n = &uni->v_normals[vi];
/* Sommets */
/* Sommets * /
vx = f_mul(v->x, rot.m[0][0]) + f_mul(v->y, rot.m[1][0]) + f_mul(v->z, rot.m[2][0]);
vy = f_mul(v->x, rot.m[0][1]) + f_mul(v->y, rot.m[1][1]) + f_mul(v->z, rot.m[2][1]);
vz = f_mul(v->x, rot.m[0][2]) + f_mul(v->y, rot.m[1][2]) + f_mul(v->z, rot.m[2][2]);
@ -91,7 +92,7 @@ void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az)
v->y = vy;
v->z = vz;
/* Normales (doivent aussi pivoter pour l'ombrage Gouraud !) */
/* Normales (doivent aussi pivoter pour l'ombrage Gouraud !) * /
vx = f_mul(n->x, rot.m[0][0]) + f_mul(n->y, rot.m[1][0]) + f_mul(n->z, rot.m[2][0]);
vy = f_mul(n->x, rot.m[0][1]) + f_mul(n->y, rot.m[1][1]) + f_mul(n->z, rot.m[2][1]);
vz = f_mul(n->x, rot.m[0][2]) + f_mul(n->y, rot.m[1][2]) + f_mul(n->z, rot.m[2][2]);
@ -100,7 +101,7 @@ void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az)
n->z = vz;
}
}
*/
void render_universe(Universe *uni, Camera *cam, int mode) {
int o, i, vi, front;
fixed rx, ry, rz, pz, nz;

View file

@ -43,6 +43,68 @@ void set_object_color(Object3D *obj, uint8_t col) {
obj->color = col;
}
/* Rotation permanente (baking) de la géométrie locale de l'objet */
void bake_object_rotation(Universe *uni, Object3D *obj, int ax, int ay, int az) {
int i;
fixed cx, sx, cy, sy, cz, sz;
fixed x, y, z, tx, ty, tz;
Vector3 *v, *n;
if (!uni || !obj) return;
/* Normalisation des angles (0-359) */
ax = ((ax % 360) + 360) % 360;
ay = ((ay % 360) + 360) % 360;
az = ((az % 360) + 360) % 360;
cx = costab[ax]; sx = sintab[ax];
cy = costab[ay]; sy = sintab[ay];
cz = costab[az]; sz = sintab[az];
for (i = 0; i < obj->num_verts; i++) {
v = &uni->verts[obj->vert_start + i];
n = &uni->v_normals[obj->vert_start + i];
/* --- Transformation du Sommet --- */
x = v->x; y = v->y; z = v->z;
if (ax) {
ty = f_mul(y, cx) - f_mul(z, sx);
tz = f_mul(y, sx) + f_mul(z, cx);
y = ty; z = tz;
}
if (ay) {
tx = f_mul(x, cy) + f_mul(z, sy);
tz = -f_mul(x, sy) + f_mul(z, cy);
x = tx; z = tz;
}
if (az) {
tx = f_mul(x, cz) - f_mul(y, sz);
ty = f_mul(x, sz) + f_mul(y, cz);
x = tx; y = ty;
}
v->x = x; v->y = y; v->z = z;
/* --- Transformation de la Normale --- */
x = n->x; y = n->y; z = n->z;
if (ax) {
ty = f_mul(y, cx) - f_mul(z, sx);
tz = f_mul(y, sx) + f_mul(z, cx);
y = ty; z = tz;
}
if (ay) {
tx = f_mul(x, cy) + f_mul(z, sy);
tz = -f_mul(x, sy) + f_mul(z, cy);
x = tx; z = tz;
}
if (az) {
tx = f_mul(x, cz) - f_mul(y, sz);
ty = f_mul(x, sz) + f_mul(y, cz);
x = tx; y = ty;
}
n->x = x; n->y = y; n->z = z;
}
}
Object3D *add_box(Universe *uni, fixed x, fixed y, fixed z, fixed dx, fixed dy, fixed dz, uint8_t col, uint8_t wire) {
int i, j, v_base;
Object3D *obj;
@ -232,6 +294,115 @@ Object3D *add_cone(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, i
return obj;
}
Object3D *add_paraboloid(Universe *uni, fixed x, fixed y, fixed z, fixed r, fixed h, int det, int rings, uint8_t col, uint8_t wire) {
int i, j, v_base_in, v_base_out;
int th, r_cur, r_next, a, b, c, d;
double t, rad, height, dy_dr, nx_base, ny_base, len, angle;
Object3D *obj;
fixed half_h = h / 2;
if (uni->num_objects >= MAX_OBJECTS) return NULL;
if (uni->num_verts + 2 * (1 + rings * det) > MAX_VERTEX) return NULL;
if (uni->num_faces + 2 * (det + (rings - 1) * det * 2) > MAX_FACES) return NULL;
obj = alloc_object(uni);
obj->color = col;
/* --- FACE INTERIEURE (Bol de l'antenne, normale vers l'avant +Y) --- */
v_base_in = uni->num_verts;
uni->v_normals[uni->num_verts].x = 0;
uni->v_normals[uni->num_verts].y = int_to_f(1);
uni->v_normals[uni->num_verts].z = 0;
uni->verts[uni->num_verts].x = x;
uni->verts[uni->num_verts].y = y - half_h;
uni->verts[uni->num_verts].z = z;
uni->num_verts++; obj->num_verts++;
for (i = 1; i <= rings; i++) {
t = (double)i / rings;
rad = ((double)r / 65536.0) * t;
height = ((double)h / 65536.0) * (t * t); /* Courbe y = x^2 */
dy_dr = 2.0 * ((double)h / 65536.0) * t / ((double)r / 65536.0); /* Dérivée */
nx_base = -dy_dr;
ny_base = 1.0;
len = sqrt(nx_base * nx_base + ny_base * ny_base);
nx_base /= len; ny_base /= len;
for (j = 0; j < det; j++) {
th = (j * 360 / det) % 360;
angle = th * 3.14159265 / 180.0;
uni->v_normals[uni->num_verts].x = (fixed)(nx_base * cos(angle) * 65536.0);
uni->v_normals[uni->num_verts].y = (fixed)(ny_base * 65536.0);
uni->v_normals[uni->num_verts].z = (fixed)(nx_base * sin(angle) * 65536.0);
uni->verts[uni->num_verts].x = x + (fixed)(rad * cos(angle) * 65536.0);
uni->verts[uni->num_verts].y = y - half_h + (fixed)(height * 65536.0);
uni->verts[uni->num_verts].z = z + (fixed)(rad * sin(angle) * 65536.0);
uni->num_verts++; obj->num_verts++;
}
}
for (j = 0; j < det; j++) {
push_face(uni, obj, v_base_in, v_base_in + 1 + ((j + 1) % det), v_base_in + 1 + j, wire);
}
for (i = 1; i < rings; i++) {
r_cur = v_base_in + 1 + (i - 1) * det;
r_next = v_base_in + 1 + i * det;
for (j = 0; j < det; j++) {
a = r_cur + j;
b = r_cur + ((j + 1) % det);
c = r_next + j;
d = r_next + ((j + 1) % det);
push_face(uni, obj, a, b, c, wire);
push_face(uni, obj, b, d, c, wire);
}
}
/* --- FACE EXTERIEURE (Dos de l'antenne, normale vers l'arrière -Y) --- */
v_base_out = uni->num_verts;
uni->v_normals[uni->num_verts].x = 0;
uni->v_normals[uni->num_verts].y = -int_to_f(1);
uni->v_normals[uni->num_verts].z = 0;
uni->verts[uni->num_verts].x = x;
uni->verts[uni->num_verts].y = y - half_h;
uni->verts[uni->num_verts].z = z;
uni->num_verts++; obj->num_verts++;
for (i = 1; i <= rings; i++) {
t = (double)i / rings;
rad = ((double)r / 65536.0) * t;
height = ((double)h / 65536.0) * (t * t);
dy_dr = 2.0 * ((double)h / 65536.0) * t / ((double)r / 65536.0);
nx_base = dy_dr; /* Normale inversée */
ny_base = -1.0;
len = sqrt(nx_base * nx_base + ny_base * ny_base);
nx_base /= len; ny_base /= len;
for (j = 0; j < det; j++) {
th = (j * 360 / det) % 360;
angle = th * 3.14159265 / 180.0;
uni->v_normals[uni->num_verts].x = (fixed)(nx_base * cos(angle) * 65536.0);
uni->v_normals[uni->num_verts].y = (fixed)(ny_base * 65536.0);
uni->v_normals[uni->num_verts].z = (fixed)(nx_base * sin(angle) * 65536.0);
uni->verts[uni->num_verts].x = x + (fixed)(rad * cos(angle) * 65536.0);
uni->verts[uni->num_verts].y = y - half_h + (fixed)(height * 65536.0);
uni->verts[uni->num_verts].z = z + (fixed)(rad * sin(angle) * 65536.0);
uni->num_verts++; obj->num_verts++;
}
}
for (j = 0; j < det; j++) {
push_face(uni, obj, v_base_out, v_base_out + 1 + j, v_base_out + 1 + ((j + 1) % det), wire); /* Winding inversé */
}
for (i = 1; i < rings; i++) {
r_cur = v_base_out + 1 + (i - 1) * det;
r_next = v_base_out + 1 + i * det;
for (j = 0; j < det; j++) {
a = r_cur + j; b = r_cur + ((j + 1) % det); c = r_next + j; d = r_next + ((j + 1) % det);
push_face(uni, obj, a, c, b, wire); /* Winding inversé */
push_face(uni, obj, b, c, d, wire);
}
}
return obj;
}
Object3D *add_plane(Universe *uni, fixed x, fixed y, fixed z, fixed w, fixed d, int seg_x, int seg_z, uint8_t col, uint8_t wire) {
int i, j, v_base;
Object3D *obj;

View file

@ -31,6 +31,80 @@ void clear_buffers(uint8_t color) {
memset(zbuffer, 0xFF, SCREEN_PIXELS * sizeof(uint16_t));
}
void clear_zbuffer(void) {
memset(zbuffer, 0xFF, SCREEN_PIXELS * sizeof(uint16_t));
}
void blit_image(Image *img, int x, int y) {
int j;
int sx, sy, dx, dy, w, h;
uint8_t *src, *dst;
if (!img || !img->data || !backbuffer) return;
sx = 0; sy = 0;
dx = x; dy = y;
w = img->width; h = img->height;
if (dx < 0) { sx = -dx; w += dx; dx = 0; }
if (dy < 0) { sy = -dy; h += dy; dy = 0; }
if (dx + w > SCREEN_W) w = SCREEN_W - dx;
if (dy + h > SCREEN_H) h = SCREEN_H - dy;
if (w <= 0 || h <= 0) return;
src = img->data + sy * img->width + sx;
dst = backbuffer + dy * SCREEN_W + dx;
for (j = 0; j < h; j++) {
memcpy(dst, src, w);
src += img->width;
dst += SCREEN_W;
}
}
void blit_image_transparent(Image *img, int x, int y, uint8_t color_key) {
int i, j;
int sx, sy, dx, dy, w, h;
uint8_t *src, *dst;
if (!img || !img->data || !backbuffer) return;
sx = 0; sy = 0;
dx = x; dy = y;
w = img->width; h = img->height;
if (dx < 0) { sx = -dx; w += dx; dx = 0; }
if (dy < 0) { sy = -dy; h += dy; dy = 0; }
if (dx + w > SCREEN_W) w = SCREEN_W - dx;
if (dy + h > SCREEN_H) h = SCREEN_H - dy;
if (w <= 0 || h <= 0) return;
src = img->data + sy * img->width + sx;
dst = backbuffer + dy * SCREEN_W + dx;
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
if (src[i] != color_key) {
dst[i] = src[i];
}
}
src += img->width;
dst += SCREEN_W;
}
}
void apply_256_palette(RGB *pal) {
int i;
outp(0x3C8, 0);
for (i = 0; i < 256; i++) {
outp(0x3C9, pal[i].r);
outp(0x3C9, pal[i].g);
outp(0x3C9, pal[i].b);
}
}
void flip(void) {
memcpy(vga, backbuffer, SCREEN_PIXELS);
}

84
src/part3D/image.c Normal file
View file

@ -0,0 +1,84 @@
#include "defines.h"
Image *create_image(int width, int height) {
Image *img = (Image *)malloc(sizeof(Image));
if (!img) return NULL;
img->width = width;
img->height = height;
img->data = (uint8_t *)malloc(width * height);
if (!img->data) {
free(img);
return NULL;
}
return img;
}
void free_image(Image *img) {
if (img) {
if (img->data) free(img->data);
free(img);
}
}
Image *load_bmp8(const char *filename, RGB *pal_out) {
FILE *f;
uint8_t header[54];
uint32_t data_offset, header_size, width, height;
int32_t h32;
uint16_t bpp;
int is_top_down = 0;
Image *img;
int i, y;
uint8_t pal_buf[1024];
f = fopen(filename, "rb");
if (!f) return NULL;
if (fread(header, 1, 54, f) != 54) { fclose(f); return NULL; }
if (header[0] != 'B' || header[1] != 'M') { fclose(f); return NULL; }
/* Lecture sécurisée (évite les soucis d'alignement mémoire x86) */
data_offset = header[10] | (header[11] << 8) | (header[12] << 16) | (header[13] << 24);
header_size = header[14] | (header[15] << 8) | (header[16] << 16) | (header[17] << 24);
width = header[18] | (header[19] << 8) | (header[20] << 16) | (header[21] << 24);
h32 = header[22] | (header[23] << 8) | (header[24] << 16) | (header[25] << 24);
bpp = header[28] | (header[29] << 8);
if (h32 < 0) { is_top_down = 1; height = -h32; }
else { height = h32; }
if (bpp != 8) { fclose(f); return NULL; }
img = create_image(width, height);
if (!img) { fclose(f); return NULL; }
if (pal_out) {
fseek(f, 14 + header_size, SEEK_SET);
fread(pal_buf, 1, 1024, f);
for (i = 0; i < 256; i++) {
/* Le DAC VGA s'attend à des valeurs de 0 à 63 (>> 2) */
pal_out[i].b = pal_buf[i*4] >> 2;
pal_out[i].g = pal_buf[i*4+1] >> 2;
pal_out[i].r = pal_buf[i*4+2] >> 2;
}
}
fseek(f, data_offset, SEEK_SET);
/* Les scanlines d'un BMP sont alignées sur des multiples de 4 octets */
{
int row_padded = (width + 3) & (~3);
uint8_t *row_data = (uint8_t *)malloc(row_padded);
if (row_data) {
for (i = 0; i < height; i++) {
y = is_top_down ? i : (height - 1 - i);
fread(row_data, 1, row_padded, f);
memcpy(&img->data[y * width], row_data, width);
}
free(row_data);
}
}
fclose(f);
return img;
}