Every other struct in the package is handed to raylib and handed back, and that proves nothing: store-and-return is symmetric, so C writes and reads the same wrong slots for any field order. An Image is different. raylib computes with it, and two computations answer differently per axis. gen-image-color takes two scalars and returns a struct reading 4, 2, 1, 7 — four distinct values in four adjacent i32 slots, with no input struct for a permutation to cancel against. Texture2D never got that: nothing without a GPU reads its width, height or mipmaps at all. And get-image-color indexes y*width + x, so on a 4-wide, 2-tall image (3,0) exists and its transpose does not. That is the axis discriminator the collision family could not be — exchange x and y in the wrapper and the read goes out of bounds. The two flips say it twice more: on two rows, one moves a mark the other leaves alone. The PNG round trip is not the symmetric trap either. stb's encoder and decoder are external ground truth; they agree with each other, not with whatever field order Flan believes in. Verified to fail, each restored after: width against height, mipmaps against format, x against y in the shim, the two flips bound to each other, and the crop rectangle's width against its height.
302 lines
13 KiB
C
302 lines
13 KiB
C
/* The C half of the raylib binding: one wrapper per `declare` in raylib.flan.
|
|
*
|
|
* The wrappers exist so that no aggregate is ever passed or returned across
|
|
* the Flan/C boundary. raylib takes Color by value and returns Vector2 by
|
|
* value, and a small aggregate is passed differently on x86-64 (<2 x float>,
|
|
* i32), on arm64, and on wasm32. Here, clang classifies each one correctly for
|
|
* whichever target the build is for; in the Flan backend it would be three
|
|
* conventions to reimplement. Everything below therefore trades in scalars and
|
|
* pointers only — see raylib.flan.
|
|
*
|
|
* raylib's own headers are not needed and not used: these prototypes are the
|
|
* declarations, so the build has no dependency on raylib-devel being
|
|
* installed, only on the shared library being linkable.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
|
|
typedef struct { float x, y; } Vector2;
|
|
typedef struct { unsigned char r, g, b, a; } Color;
|
|
typedef struct { unsigned int id; int width, height, mipmaps, format; } Texture2D;
|
|
typedef struct { float x, y, width, height; } Rectangle;
|
|
typedef struct { Vector2 offset, target; float rotation, zoom; } Camera2D;
|
|
|
|
extern void InitWindow(int width, int height, const char *title);
|
|
extern void CloseWindow(void);
|
|
extern bool WindowShouldClose(void);
|
|
extern void SetTargetFPS(int fps);
|
|
extern void SetTraceLogLevel(int level);
|
|
extern bool IsKeyPressed(int key);
|
|
extern bool IsKeyDown(int key);
|
|
extern bool IsKeyReleased(int key);
|
|
extern bool IsMouseButtonPressed(int button);
|
|
extern bool IsMouseButtonDown(int button);
|
|
extern bool IsMouseButtonReleased(int button);
|
|
extern Vector2 GetMousePosition(void);
|
|
extern Color GetColor(unsigned int hex);
|
|
extern void BeginDrawing(void);
|
|
extern void EndDrawing(void);
|
|
extern void DrawFPS(int x, int y);
|
|
extern void ClearBackground(Color color);
|
|
extern void DrawRectangle(int x, int y, int width, int height, Color color);
|
|
extern void SetShapesTexture(Texture2D texture, Rectangle source);
|
|
extern Texture2D GetShapesTexture(void);
|
|
extern Rectangle GetShapesTextureRectangle(void);
|
|
extern Rectangle GetCollisionRec(Rectangle a, Rectangle b);
|
|
extern Texture2D LoadTexture(const char *fileName);
|
|
extern bool IsTextureValid(Texture2D texture);
|
|
extern void UnloadTexture(Texture2D texture);
|
|
extern void DrawTexture(Texture2D texture, int posX, int posY, Color tint);
|
|
extern void DrawTextureV(Texture2D texture, Vector2 position, Color tint);
|
|
extern void DrawTextureEx(Texture2D texture, Vector2 position, float rotation,
|
|
float scale, Color tint);
|
|
extern void DrawTextureRec(Texture2D texture, Rectangle source,
|
|
Vector2 position, Color tint);
|
|
extern void BeginMode2D(Camera2D camera);
|
|
extern void EndMode2D(void);
|
|
extern Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera);
|
|
extern Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera);
|
|
extern bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);
|
|
extern bool CheckCollisionCircles(Vector2 c1, float r1, Vector2 c2, float r2);
|
|
extern bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec);
|
|
extern bool CheckCollisionCircleLine(Vector2 center, float radius,
|
|
Vector2 p1, Vector2 p2);
|
|
extern bool CheckCollisionPointRec(Vector2 point, Rectangle rec);
|
|
extern bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius);
|
|
extern bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2,
|
|
Vector2 p3);
|
|
extern bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2,
|
|
int threshold);
|
|
extern bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points,
|
|
int pointCount);
|
|
extern bool CheckCollisionLines(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2,
|
|
Vector2 *collisionPoint);
|
|
|
|
/* A Flan string arrives as ptr+len and is not NUL-terminated, so a C API that
|
|
* wants a C string needs a copy. Two callers want one: the window title and a
|
|
* texture's file path. Each passes a buffer big enough for what it is — 256
|
|
* for a title, PATH_MAX for a path — and truncating is better than reading
|
|
* past the end. A truncated path fails to open and LoadTexture returns an id
|
|
* of 0, which is what texture-valid? is for. */
|
|
static const char *cstr(const char *p, long long n, char *buf, size_t cap) {
|
|
size_t k = (size_t)n < cap - 1 ? (size_t)n : cap - 1;
|
|
memcpy(buf, p, k);
|
|
buf[k] = '\0';
|
|
return buf;
|
|
}
|
|
|
|
void flan_rl_init_window(int width, int height, const char *title, long long n) {
|
|
char buf[256];
|
|
InitWindow(width, height, cstr(title, n, buf, sizeof buf));
|
|
}
|
|
|
|
void flan_rl_close_window(void) { CloseWindow(); }
|
|
bool flan_rl_window_should_close(void) { return WindowShouldClose(); }
|
|
void flan_rl_set_target_fps(int fps) { SetTargetFPS(fps); }
|
|
void flan_rl_set_trace_log_level(int l) { SetTraceLogLevel(l); }
|
|
|
|
bool flan_rl_is_key_pressed(int key) { return IsKeyPressed(key); }
|
|
bool flan_rl_is_key_down(int key) { return IsKeyDown(key); }
|
|
bool flan_rl_is_key_released(int key) { return IsKeyReleased(key); }
|
|
|
|
bool flan_rl_is_mouse_button_pressed(int b) { return IsMouseButtonPressed(b); }
|
|
bool flan_rl_is_mouse_button_down(int b) { return IsMouseButtonDown(b); }
|
|
bool flan_rl_is_mouse_button_released(int b) { return IsMouseButtonReleased(b); }
|
|
|
|
void flan_rl_get_mouse_position(Vector2 *out) { *out = GetMousePosition(); }
|
|
|
|
void flan_rl_get_color(unsigned int hex, Color *out) { *out = GetColor(hex); }
|
|
|
|
void flan_rl_begin_drawing(void) { BeginDrawing(); }
|
|
void flan_rl_end_drawing(void) { EndDrawing(); }
|
|
void flan_rl_draw_fps(int x, int y) { DrawFPS(x, y); }
|
|
|
|
void flan_rl_clear_background(const Color *color) { ClearBackground(*color); }
|
|
|
|
void flan_rl_draw_rectangle(int x, int y, int width, int height,
|
|
const Color *color) {
|
|
DrawRectangle(x, y, width, height, *color);
|
|
}
|
|
|
|
void flan_rl_set_shapes_texture(const Texture2D *texture, const Rectangle *source) {
|
|
SetShapesTexture(*texture, *source);
|
|
}
|
|
|
|
void flan_rl_get_shapes_texture(Texture2D *out) { *out = GetShapesTexture(); }
|
|
|
|
void flan_rl_get_shapes_texture_rectangle(Rectangle *out) {
|
|
*out = GetShapesTextureRectangle();
|
|
}
|
|
|
|
void flan_rl_get_collision_rec(const Rectangle *a, const Rectangle *b,
|
|
Rectangle *out) {
|
|
*out = GetCollisionRec(*a, *b);
|
|
}
|
|
|
|
void flan_rl_load_texture(const char *path, long long n, Texture2D *out) {
|
|
char buf[PATH_MAX];
|
|
*out = LoadTexture(cstr(path, n, buf, sizeof buf));
|
|
}
|
|
|
|
bool flan_rl_is_texture_valid(const Texture2D *texture) {
|
|
return IsTextureValid(*texture);
|
|
}
|
|
|
|
void flan_rl_unload_texture(const Texture2D *texture) { UnloadTexture(*texture); }
|
|
|
|
void flan_rl_draw_texture(const Texture2D *texture, int x, int y,
|
|
const Color *tint) {
|
|
DrawTexture(*texture, x, y, *tint);
|
|
}
|
|
|
|
void flan_rl_draw_texture_v(const Texture2D *texture, const Vector2 *position,
|
|
const Color *tint) {
|
|
DrawTextureV(*texture, *position, *tint);
|
|
}
|
|
|
|
void flan_rl_draw_texture_ex(const Texture2D *texture, const Vector2 *position,
|
|
float rotation, float scale, const Color *tint) {
|
|
DrawTextureEx(*texture, *position, rotation, scale, *tint);
|
|
}
|
|
|
|
void flan_rl_draw_texture_rec(const Texture2D *texture, const Rectangle *source,
|
|
const Vector2 *position, const Color *tint) {
|
|
DrawTextureRec(*texture, *source, *position, *tint);
|
|
}
|
|
|
|
void flan_rl_begin_mode_2d(const Camera2D *camera) { BeginMode2D(*camera); }
|
|
void flan_rl_end_mode_2d(void) { EndMode2D(); }
|
|
|
|
void flan_rl_get_screen_to_world_2d(const Vector2 *position,
|
|
const Camera2D *camera, Vector2 *out) {
|
|
*out = GetScreenToWorld2D(*position, *camera);
|
|
}
|
|
|
|
void flan_rl_get_world_to_screen_2d(const Vector2 *position,
|
|
const Camera2D *camera, Vector2 *out) {
|
|
*out = GetWorldToScreen2D(*position, *camera);
|
|
}
|
|
|
|
bool flan_rl_check_collision_recs(const Rectangle *a, const Rectangle *b) {
|
|
return CheckCollisionRecs(*a, *b);
|
|
}
|
|
|
|
bool flan_rl_check_collision_circles(const Vector2 *c1, float r1,
|
|
const Vector2 *c2, float r2) {
|
|
return CheckCollisionCircles(*c1, r1, *c2, r2);
|
|
}
|
|
|
|
bool flan_rl_check_collision_circle_rec(const Vector2 *center, float radius,
|
|
const Rectangle *rec) {
|
|
return CheckCollisionCircleRec(*center, radius, *rec);
|
|
}
|
|
|
|
bool flan_rl_check_collision_circle_line(const Vector2 *center, float radius,
|
|
const Vector2 *p1, const Vector2 *p2) {
|
|
return CheckCollisionCircleLine(*center, radius, *p1, *p2);
|
|
}
|
|
|
|
bool flan_rl_check_collision_point_rec(const Vector2 *point, const Rectangle *rec) {
|
|
return CheckCollisionPointRec(*point, *rec);
|
|
}
|
|
|
|
bool flan_rl_check_collision_point_circle(const Vector2 *point,
|
|
const Vector2 *center, float radius) {
|
|
return CheckCollisionPointCircle(*point, *center, radius);
|
|
}
|
|
|
|
bool flan_rl_check_collision_point_triangle(const Vector2 *point, const Vector2 *a,
|
|
const Vector2 *b, const Vector2 *c) {
|
|
return CheckCollisionPointTriangle(*point, *a, *b, *c);
|
|
}
|
|
|
|
bool flan_rl_check_collision_point_line(const Vector2 *point, const Vector2 *p1,
|
|
const Vector2 *p2, int threshold) {
|
|
return CheckCollisionPointLine(*point, *p1, *p2, threshold);
|
|
}
|
|
|
|
/* A Flan slice arrives as ptr+len, the same shape a string does. raylib wants
|
|
* an int count, and a polygon with more than INT_MAX points is not a thing
|
|
* that happens; the clamp is there so the conversion is not silent. */
|
|
bool flan_rl_check_collision_point_poly(const Vector2 *point,
|
|
const Vector2 *points, long long n) {
|
|
if (n > INT_MAX) n = INT_MAX;
|
|
return CheckCollisionPointPoly(*point, points, (int)n);
|
|
}
|
|
|
|
bool flan_rl_check_collision_lines(const Vector2 *a1, const Vector2 *a2,
|
|
const Vector2 *b1, const Vector2 *b2,
|
|
Vector2 *out) {
|
|
return CheckCollisionLines(*a1, *a2, *b1, *b2, out);
|
|
}
|
|
|
|
/* ── Images ─────────────────────────────────────────────────────────
|
|
*
|
|
* An Image is pixels in RAM, so all of this runs with no window and no GL
|
|
* context — which is why it is the part of the package that a headless test
|
|
* can actually assert rather than merely link. `data` is the pixel buffer
|
|
* raylib owns; `format` is a PixelFormat enum and GenImageColor makes 7
|
|
* (uncompressed R8G8B8A8).
|
|
*
|
|
* raylib 5.5 spells the predicate IsImageValid. IsImageReady, which the 5.1
|
|
* header still had, is gone — checked with nm -D, not remembered.
|
|
*/
|
|
typedef struct { void *data; int width, height, mipmaps, format; } Image;
|
|
|
|
extern Image LoadImage(const char *fileName);
|
|
extern bool IsImageValid(Image image);
|
|
extern void UnloadImage(Image image);
|
|
extern bool ExportImage(Image image, const char *fileName);
|
|
extern Image GenImageColor(int width, int height, Color color);
|
|
extern void ImageResize(Image *image, int newWidth, int newHeight);
|
|
extern void ImageResizeNN(Image *image, int newWidth, int newHeight);
|
|
extern void ImageCrop(Image *image, Rectangle crop);
|
|
extern void ImageFlipHorizontal(Image *image);
|
|
extern void ImageFlipVertical(Image *image);
|
|
extern void ImageDrawPixel(Image *dst, int posX, int posY, Color color);
|
|
extern Color GetImageColor(Image image, int x, int y);
|
|
extern Texture2D LoadTextureFromImage(Image image);
|
|
|
|
void flan_rl_load_image(const char *path, long long n, Image *out) {
|
|
char buf[PATH_MAX];
|
|
*out = LoadImage(cstr(path, n, buf, sizeof buf));
|
|
}
|
|
|
|
bool flan_rl_is_image_valid(const Image *image) { return IsImageValid(*image); }
|
|
void flan_rl_unload_image(const Image *image) { UnloadImage(*image); }
|
|
|
|
bool flan_rl_export_image(const Image *image, const char *path, long long n) {
|
|
char buf[PATH_MAX];
|
|
return ExportImage(*image, cstr(path, n, buf, sizeof buf));
|
|
}
|
|
|
|
void flan_rl_gen_image_color(int width, int height, const Color *color,
|
|
Image *out) {
|
|
*out = GenImageColor(width, height, *color);
|
|
}
|
|
|
|
void flan_rl_image_resize(Image *image, int w, int h) { ImageResize(image, w, h); }
|
|
void flan_rl_image_resize_nn(Image *image, int w, int h) { ImageResizeNN(image, w, h); }
|
|
|
|
void flan_rl_image_crop(Image *image, const Rectangle *crop) {
|
|
ImageCrop(image, *crop);
|
|
}
|
|
|
|
void flan_rl_image_flip_horizontal(Image *image) { ImageFlipHorizontal(image); }
|
|
void flan_rl_image_flip_vertical(Image *image) { ImageFlipVertical(image); }
|
|
|
|
void flan_rl_image_draw_pixel(Image *dst, int x, int y, const Color *color) {
|
|
ImageDrawPixel(dst, x, y, *color);
|
|
}
|
|
|
|
void flan_rl_get_image_color(const Image *image, int x, int y, Color *out) {
|
|
*out = GetImageColor(*image, x, y);
|
|
}
|
|
|
|
void flan_rl_load_texture_from_image(const Image *image, Texture2D *out) {
|
|
*out = LoadTextureFromImage(*image);
|
|
}
|