Textures and sprites, and a test that can actually fail
Eleven bindings: the two structs raylib needs for them, load/valid?/unload, four draw forms, and the shapes-texture and collision-rectangle calls. IsTextureReady does not exist in raylib 5.5 - it was renamed - so IsTextureValid is what is bound; the old name would have been a link error. The test is the point. The obvious one - hand raylib a Texture2D, read it back, compare - passes for any layout, because store-and-return is symmetric and C writes and reads the same wrong slots. Permuting two fields in the defstruct produced identical output. What replaced it makes raylib compute something from the fields: GetCollisionRec pins Rectangle completely, four numbers from four different field pairs, and SetShapesTexture's default substitution pins the id against the rest of Texture2D. Each was verified by permuting fields and watching the test fail. The limit is stated where someone will find it: width, height and mipmaps are not pinned against each other, because nothing raylib computes without a GL context reads them. A width/height swap shows only as a visibly wrong sprite. That half is verified by running sand under Xvfb and looking, which is recorded as manual and not asserted anywhere.
This commit is contained in:
commit
dd556bfda2
24
NEXT.md
24
NEXT.md
@ -237,14 +237,30 @@ little-endian reading of the packed integer, so an identity would have passed a
|
||||
weaker test. That case is in the acceptance table, skipped if `libraylib` is
|
||||
not installed.
|
||||
|
||||
The bindings are 18 calls: window (`init-window`, `close-window`,
|
||||
The bindings are 29 calls: window (`init-window`, `close-window`,
|
||||
`window-should-close?`, `set-target-fps`, `set-trace-log-level`), keyboard
|
||||
(`key-pressed?`/`down?`/`released?`), mouse (`mouse-button-pressed?`/`down?`/
|
||||
`released?`, `get-mouse-position`), `get-color`, and drawing (`begin-drawing`,
|
||||
`end-drawing`, `draw-fps`, `clear-background`, `draw-rectangle`), plus the
|
||||
`Key`, `MouseButton` and `TraceLogLevel` enums. Adding one is three lines: a
|
||||
`released?`, `get-mouse-position`), `get-color`, drawing (`begin-drawing`,
|
||||
`end-drawing`, `draw-fps`, `clear-background`, `draw-rectangle`), textures
|
||||
(`load-texture`, `texture-valid?`, `unload-texture`, `draw-texture`,
|
||||
`draw-texture-v`, `draw-texture-ex`, `draw-texture-rec`), and the shapes
|
||||
texture and rectangle intersection (`set-shapes-texture`,
|
||||
`get-shapes-texture`, `get-shapes-texture-rectangle`, `get-collision-rec`),
|
||||
plus the `Key`, `MouseButton` and `TraceLogLevel` enums and the `Vector2`,
|
||||
`Color`, `Texture2D` and `Rectangle` structs. Adding one is three lines: a
|
||||
`declare`, an `extern` prototype, and a one-line wrapper.
|
||||
|
||||
The texture calls are the first ones with no headless test, because loading
|
||||
one needs a GL context. What the acceptance case does instead is pin the two
|
||||
new struct layouts using the only things raylib computes from those fields
|
||||
without a GPU: `GetCollisionRec`, which pins `Rectangle` completely, and
|
||||
`SetShapesTexture`'s default substitution, which pins `Texture2D`'s `id` and
|
||||
`format` and nothing else. Handing a struct over and reading it back proves
|
||||
nothing at all — storing and returning is symmetric, so a permuted layout
|
||||
comes back permuted the same way and the case passes. `width`, `height` and
|
||||
`mipmaps` are therefore checked only by looking at `sand.flan` running, which
|
||||
draws the brush sprite four ways for that reason.
|
||||
|
||||
No raylib headers are needed: `shim.c` declares the prototypes it uses, so the
|
||||
build depends on the shared library being linkable and not on `raylib-devel`.
|
||||
`vendor/raylib/link` carries `-l:libraylib.so.550` because Fedora ships the
|
||||
|
||||
39
sand.flan
39
sand.flan
@ -28,6 +28,40 @@
|
||||
(import sim "sand-sim") ; no collection prefix: relative to this file
|
||||
(import agent "vendor:agent") ; the dev agent: redefinitions, installed below
|
||||
|
||||
;; The brush sprite: a 16x8 sheet of two 8x8 frames, the ring drawn while the
|
||||
;; mouse is idle and the blob while it is painting. It is here because the
|
||||
;; texture calls cannot be in the acceptance table at all — loading one needs a
|
||||
;; GL context — so the only way they are exercised is by running this.
|
||||
(defvar brush rl/Texture2D)
|
||||
(defvar brush-ok bool)
|
||||
|
||||
;; A missing file is not a crash and not silence: LoadTexture hands back a
|
||||
;; texture with an id of 0, every draw with it is a no-op, and the program
|
||||
;; looks like it has a drawing bug. So it is asked and said once, here.
|
||||
(defn load-brush []
|
||||
(set brush (rl/load-texture "brush.png"))
|
||||
(set brush-ok (rl/texture-valid? brush))
|
||||
(unless brush-ok
|
||||
(print-line "sand: cannot load brush.png — drawing the cursor is off")))
|
||||
|
||||
;; Four draws, one per shape the call comes in, because none of them can be in
|
||||
;; the acceptance table. The cursor picks one frame out of the sheet and so
|
||||
;; needs a source rectangle; the three badges in the corner are the whole
|
||||
;; sheet, at integer coordinates, at a Vector2 tinted with the current sand
|
||||
;; colour, and scaled up — draw-texture, draw-texture-v and draw-texture-ex.
|
||||
(defn draw-brush []
|
||||
(when brush-ok
|
||||
(let [m (rl/get-mouse-position)
|
||||
frame (f32 (if (rl/mouse-button-down? :left) 8.0 0.0))]
|
||||
(rl/draw-texture-rec brush
|
||||
(rl/Rectangle {:x frame :y 0.0 :width 8.0 :height 8.0})
|
||||
(rl/Vector2 {:x (- (.x m) 4.0) :y (- (.y m) 4.0)})
|
||||
rl/white)
|
||||
(rl/draw-texture brush 20 50 rl/white)
|
||||
(rl/draw-texture-v brush (rl/Vector2 {:x 44.0 :y 50.0})
|
||||
(rl/get-color (nth sim/colors sim/current-color)))
|
||||
(rl/draw-texture-ex brush (rl/Vector2 {:x 72.0 :y 46.0}) 0.0 2.0 rl/white))))
|
||||
|
||||
;; Locals are assignable places (spec-memory.md); parameters are not.
|
||||
(defn paint []
|
||||
(let [m (rl/get-mouse-position)
|
||||
@ -61,6 +95,7 @@
|
||||
(i32 (* row sim/cell-size))
|
||||
sim/cell-size sim/cell-size
|
||||
(rl/get-color c))))))
|
||||
(draw-brush)
|
||||
(rl/draw-fps 20 20))
|
||||
|
||||
(defn main []
|
||||
@ -68,6 +103,10 @@
|
||||
(rl/init-window sim/screen-width sim/screen-height "SAND")
|
||||
(defer (rl/close-window))
|
||||
(rl/set-target-fps 120)
|
||||
;; After the window, never before: LoadTexture uploads to the GPU and there
|
||||
;; is no GPU to upload to until InitWindow has made a context.
|
||||
(load-brush)
|
||||
(defer (rl/unload-texture brush))
|
||||
;; The dev agent listens on a socket for redefinitions and hands them over;
|
||||
;; (agent/poll) below is where they are installed. Building without --dev is
|
||||
;; fine — nothing has cells to install into, so a module is refused on the
|
||||
|
||||
@ -1,12 +1,74 @@
|
||||
(import rl "vendor:raylib")
|
||||
|
||||
;; No window: GetColor and the enums are pure, so this exercises the whole
|
||||
;; boundary — struct out-pointer, keyword->enum, string ptr+len — headlessly.
|
||||
;; The raylib boundary, headless. GetColor, the shapes texture and rectangle
|
||||
;; intersection all need no window, so the whole crossing — a struct out of C
|
||||
;; through an out-pointer, a struct into C through a pointer, a keyword
|
||||
;; resolved against an enum — is exercised without a display.
|
||||
;;
|
||||
;; What is being checked is that a struct's FIELDS mean the same thing on both
|
||||
;; sides. Note what does not check that: handing raylib a struct and reading it
|
||||
;; back, because storing and returning is symmetric and a permuted layout
|
||||
;; survives it unchanged. Every case below is asymmetric — raylib does
|
||||
;; something to the fields that depends on which is which.
|
||||
|
||||
(defn show-texture [t rl/Texture2D]
|
||||
(print-i64 (i64 (.id t))) (newline)
|
||||
(print-i64 (i64 (.width t))) (newline)
|
||||
(print-i64 (i64 (.height t))) (newline)
|
||||
(print-i64 (i64 (.mipmaps t))) (newline)
|
||||
(print-i64 (i64 (.format t))) (newline))
|
||||
|
||||
(defn show-rect [r rl/Rectangle]
|
||||
(print-f64 (f64 (.x r))) (newline)
|
||||
(print-f64 (f64 (.y r))) (newline)
|
||||
(print-f64 (f64 (.width r))) (newline)
|
||||
(print-f64 (f64 (.height r))) (newline))
|
||||
|
||||
(defn main [] i32
|
||||
(rl/set-trace-log-level :warning)
|
||||
|
||||
;; A Color is four bytes in RGBA order, so 0x11223344 is 17 34 51 68 and not
|
||||
;; the little-endian reading of the packed integer. An identity would pass a
|
||||
;; weaker test than this one.
|
||||
(let [c (rl/get-color 0x11223344)]
|
||||
(print-i64 (i64 (.r c))) (newline)
|
||||
(print-i64 (i64 (.g c))) (newline)
|
||||
(print-i64 (i64 (.b c))) (newline)
|
||||
(print-i64 (i64 (.a c))) (newline)
|
||||
(rl/set-trace-log-level :warning)
|
||||
0))
|
||||
(print-i64 (i64 (.a c))) (newline))
|
||||
|
||||
;; Rectangle, pinned completely. The intersection of (0,0,10,4) and
|
||||
;; (6,1,10,10) is (6,1,4,3) — four different numbers, each derived from a
|
||||
;; different pair of fields, so swapping any two fields changes the answer.
|
||||
(show-rect (rl/get-collision-rec (rl/Rectangle {:x 0.0 :y 0.0 :width 10.0 :height 4.0})
|
||||
(rl/Rectangle {:x 6.0 :y 1.0 :width 10.0 :height 10.0})))
|
||||
|
||||
;; Texture2D, as far as a machine with no GPU can go. raylib keeps the
|
||||
;; shapes texture without touching GL, and substitutes a default when
|
||||
;; `texture.id`, `source.width` or `source.height` is zero — that guard is
|
||||
;; the only asymmetry a headless test gets.
|
||||
(let [rect (rl/Rectangle {:x 3.5 :y 7.25 :width 11.5 :height 13.75})]
|
||||
;; (A) Valid, five distinct values: they come back, so the struct crosses
|
||||
;; intact in both directions and raylib stored it rather than defaulting.
|
||||
(rl/set-shapes-texture (rl/Texture2D {:id 7 :width 13 :height 17 :mipmaps 2 :format 4}) rect)
|
||||
(show-texture (rl/get-shapes-texture))
|
||||
(show-rect (rl/get-shapes-texture-rectangle))
|
||||
|
||||
;; (B) id zero, everything else positive: the default 1 1 1 1 7 comes
|
||||
;; back. The 7 is the only distinct field in it, so this pins `format` as
|
||||
;; the last field, and the substitution happening at all pins `id` as the
|
||||
;; field the guard reads.
|
||||
(rl/set-shapes-texture (rl/Texture2D {:id 0 :width 13 :height 17 :mipmaps 2 :format 4}) rect)
|
||||
(show-texture (rl/get-shapes-texture))
|
||||
|
||||
;; (C) width zero, id positive: still stored, because the guard does not
|
||||
;; look at the texture's width. Without this case, (B) would pass just as
|
||||
;; well with `id` and `width` swapped — the zero would land in the guarded
|
||||
;; slot either way.
|
||||
;;
|
||||
;; That is the limit of what is checkable here: nothing raylib computes
|
||||
;; without a GL context reads width, height or mipmaps, so their order
|
||||
;; among themselves is not pinned by this test. A swap there shows up as a
|
||||
;; visibly wrong sprite in the interactive run, and nowhere else.
|
||||
(rl/set-shapes-texture (rl/Texture2D {:id 7 :width 0 :height 17 :mipmaps 2 :format 4}) rect)
|
||||
(show-texture (rl/get-shapes-texture)))
|
||||
0)
|
||||
|
||||
@ -142,14 +142,38 @@ let () =
|
||||
text code
|
||||
end;
|
||||
|
||||
(* The raylib FFI, headless. GetColor and the enums need no window, so the
|
||||
whole boundary is exercised without a display: a struct returned through
|
||||
an out-pointer, a keyword resolved against an enum, and a Flan string
|
||||
crossing as ptr+len. 0x11223344 comes back as four separate bytes, which
|
||||
is the check that matters — a Color is not the little-endian reading of
|
||||
the packed integer, so an identity would pass a weaker test. *)
|
||||
if Sys.command "ldconfig -p 2>/dev/null | grep -q libraylib" = 0 then
|
||||
outputs "raylib ffi, headless" "programs/raylib-ffi.flan" "17\n34\n51\n68\n"
|
||||
(* The raylib FFI, headless. GetColor, rectangle intersection and the
|
||||
shapes texture need no window, so the whole boundary is exercised
|
||||
without a display: a struct out of C through an out-pointer, a struct
|
||||
into C through a pointer, a keyword resolved against an enum, and a
|
||||
Flan string crossing as ptr+len.
|
||||
|
||||
Every case is asymmetric, which is the point. 0x11223344 comes back as
|
||||
four separate bytes, so a Color is not the little-endian reading of the
|
||||
packed integer. The intersection of (0,0,10,4) and (6,1,10,10) is
|
||||
(6,1,4,3), four numbers from four different pairs of fields, so no
|
||||
permutation of Rectangle survives it. And the shapes texture is stored
|
||||
or replaced by 1 1 1 1 7 depending on which field is zero, which pins
|
||||
Texture2D's id and format. Handing raylib a struct and reading it back
|
||||
would have passed with any of those permuted — storing and returning is
|
||||
symmetric. What the last case cannot pin, because nothing raylib
|
||||
computes without a GL context reads them, is width, height and mipmaps
|
||||
against each other. *)
|
||||
let raylib_out =
|
||||
"17\n34\n51\n68\n\
|
||||
6\n1\n4\n3\n\
|
||||
7\n13\n17\n2\n4\n3.5\n7.25\n11.5\n13.75\n\
|
||||
1\n1\n1\n1\n7\n\
|
||||
7\n0\n17\n2\n4\n"
|
||||
in
|
||||
if Sys.command "ldconfig -p 2>/dev/null | grep -q libraylib" = 0 then begin
|
||||
outputs "raylib ffi, headless" "programs/raylib-ffi.flan" raylib_out;
|
||||
(* And at -O0, for the reason the rest of the table is: every struct
|
||||
here crosses as (addr v) on a local, which is the alloca mem2reg
|
||||
would launder before anyone noticed it was wrong. *)
|
||||
outputs ~opt:"-O0" "raylib ffi, headless, -O0" "programs/raylib-ffi.flan"
|
||||
raylib_out
|
||||
end
|
||||
else
|
||||
print_endline "acceptance: skipping the raylib FFI case (no libraylib)";
|
||||
|
||||
|
||||
133
vendor/raylib/raylib.flan
vendored
133
vendor/raylib/raylib.flan
vendored
@ -20,6 +20,12 @@
|
||||
(defstruct Vector2 [x f32 y f32])
|
||||
(defstruct Color [r u8 g u8 b u8 a u8])
|
||||
|
||||
;; Texture2D is five 4-byte fields in a row, which is the layout most likely
|
||||
;; to be silently wrong: permute two of them and every field still reads as a
|
||||
;; plausible number. Rectangle is four floats in x/y/width/height order.
|
||||
(defstruct Texture2D [id u32 width i32 height i32 mipmaps i32 format i32])
|
||||
(defstruct Rectangle [x f32 y f32 width f32 height f32])
|
||||
|
||||
;; KeyboardKey, the subset sand.flan uses. A keyword at a call site resolves
|
||||
;; against these members at compile time and a typo is an error there.
|
||||
(defenum Key
|
||||
@ -103,3 +109,130 @@
|
||||
(defn draw-rectangle [x i32 y i32 width i32 height i32 color Color]
|
||||
(let [c color]
|
||||
(draw-rectangle-raw x y width height (addr c))))
|
||||
|
||||
;; ── Shapes texture ──────────────────────────────────────────────────
|
||||
;;
|
||||
;; raylib draws every shape from one atlas texture, and this pair sets and
|
||||
;; reads it. It is bound here for a second reason: it is the only part of the
|
||||
;; API that stores a Texture2D and a Rectangle and hands them back without
|
||||
;; touching the GPU, so it is how the acceptance table checks both layouts
|
||||
;; headlessly. Everything else that takes a texture needs a GL context.
|
||||
;;
|
||||
;; raylib substitutes a default ({1,1,1,1,7} / {0,0,1,1}) when the id or the
|
||||
;; source's width or height is not positive, so a caller — and the test —
|
||||
;; should keep clear of those values if it wants its own back.
|
||||
|
||||
(declare set-shapes-texture-raw
|
||||
[texture (Ptr Texture2D) source (Ptr Rectangle)]
|
||||
"flan_rl_set_shapes_texture")
|
||||
|
||||
(defn set-shapes-texture [texture Texture2D source Rectangle]
|
||||
(let [t texture
|
||||
r source]
|
||||
(set-shapes-texture-raw (addr t) (addr r))))
|
||||
|
||||
(declare get-shapes-texture-raw [out (Ptr Texture2D)] "flan_rl_get_shapes_texture")
|
||||
|
||||
(defn get-shapes-texture [] Texture2D
|
||||
(let [t (Texture2D {})]
|
||||
(get-shapes-texture-raw (addr t))
|
||||
t))
|
||||
|
||||
(declare get-shapes-texture-rectangle-raw
|
||||
[out (Ptr Rectangle)] "flan_rl_get_shapes_texture_rectangle")
|
||||
|
||||
(defn get-shapes-texture-rectangle [] Rectangle
|
||||
(let [r (Rectangle {})]
|
||||
(get-shapes-texture-rectangle-raw (addr r))
|
||||
r))
|
||||
|
||||
;; ── Shapes ──────────────────────────────────────────────────────────
|
||||
;;
|
||||
;; Rectangle intersection, which raylib computes from all four fields in
|
||||
;; different ways. It is the one Rectangle call that needs no GPU, so it is
|
||||
;; also how the acceptance table pins the layout: a store-and-return check is
|
||||
;; symmetric and a permuted layout survives it untouched.
|
||||
|
||||
(declare get-collision-rec-raw
|
||||
[a (Ptr Rectangle) b (Ptr Rectangle) out (Ptr Rectangle)]
|
||||
"flan_rl_get_collision_rec")
|
||||
|
||||
(defn get-collision-rec [a Rectangle b Rectangle] Rectangle
|
||||
(let [x a
|
||||
y b
|
||||
out (Rectangle {})]
|
||||
(get-collision-rec-raw (addr x) (addr y) (addr out))
|
||||
out))
|
||||
|
||||
;; ── Textures ────────────────────────────────────────────────────────
|
||||
;;
|
||||
;; Everything here needs a GL context, so a window has to be open first —
|
||||
;; load-texture before init-window returns an id of 0 and raylib says so on
|
||||
;; the log. texture-valid? is how that is noticed in the program rather than
|
||||
;; only in the log; raylib 5.5 spells it IsTextureValid, and IsTextureReady,
|
||||
;; which older code calls, does not exist in this version.
|
||||
|
||||
(declare load-texture-raw [path string out (Ptr Texture2D)] "flan_rl_load_texture")
|
||||
|
||||
(defn load-texture [path string] Texture2D
|
||||
(let [t (Texture2D {})]
|
||||
(load-texture-raw path (addr t))
|
||||
t))
|
||||
|
||||
(declare texture-valid?-raw [texture (Ptr Texture2D)] bool "flan_rl_is_texture_valid")
|
||||
|
||||
(defn texture-valid? [texture Texture2D] bool
|
||||
(let [t texture]
|
||||
(texture-valid?-raw (addr t))))
|
||||
|
||||
(declare unload-texture-raw [texture (Ptr Texture2D)] "flan_rl_unload_texture")
|
||||
|
||||
(defn unload-texture [texture Texture2D]
|
||||
(let [t texture]
|
||||
(unload-texture-raw (addr t))))
|
||||
|
||||
(declare draw-texture-raw
|
||||
[texture (Ptr Texture2D) x i32 y i32 tint (Ptr Color)]
|
||||
"flan_rl_draw_texture")
|
||||
|
||||
(defn draw-texture [texture Texture2D x i32 y i32 tint Color]
|
||||
(let [t texture
|
||||
c tint]
|
||||
(draw-texture-raw (addr t) x y (addr c))))
|
||||
|
||||
(declare draw-texture-v-raw
|
||||
[texture (Ptr Texture2D) position (Ptr Vector2) tint (Ptr Color)]
|
||||
"flan_rl_draw_texture_v")
|
||||
|
||||
(defn draw-texture-v [texture Texture2D position Vector2 tint Color]
|
||||
(let [t texture
|
||||
p position
|
||||
c tint]
|
||||
(draw-texture-v-raw (addr t) (addr p) (addr c))))
|
||||
|
||||
(declare draw-texture-ex-raw
|
||||
[texture (Ptr Texture2D) position (Ptr Vector2) rotation f32 scale f32
|
||||
tint (Ptr Color)]
|
||||
"flan_rl_draw_texture_ex")
|
||||
|
||||
(defn draw-texture-ex [texture Texture2D position Vector2 rotation f32
|
||||
scale f32 tint Color]
|
||||
(let [t texture
|
||||
p position
|
||||
c tint]
|
||||
(draw-texture-ex-raw (addr t) (addr p) rotation scale (addr c))))
|
||||
|
||||
;; A negative source width or height flips the sprite, which is how a sheet is
|
||||
;; drawn facing the other way without a second image.
|
||||
(declare draw-texture-rec-raw
|
||||
[texture (Ptr Texture2D) source (Ptr Rectangle) position (Ptr Vector2)
|
||||
tint (Ptr Color)]
|
||||
"flan_rl_draw_texture_rec")
|
||||
|
||||
(defn draw-texture-rec [texture Texture2D source Rectangle position Vector2
|
||||
tint Color]
|
||||
(let [t texture
|
||||
s source
|
||||
p position
|
||||
c tint]
|
||||
(draw-texture-rec-raw (addr t) (addr s) (addr p) (addr c))))
|
||||
|
||||
69
vendor/raylib/shim.c
vendored
69
vendor/raylib/shim.c
vendored
@ -15,10 +15,13 @@
|
||||
|
||||
#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;
|
||||
|
||||
extern void InitWindow(int width, int height, const char *title);
|
||||
extern void CloseWindow(void);
|
||||
@ -38,10 +41,26 @@ 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);
|
||||
|
||||
/* A Flan string arrives as ptr+len and is not NUL-terminated, so a C API that
|
||||
* wants a C string needs a copy. The window title is the only one, it is short
|
||||
* by nature, and truncating is better than reading past the end. */
|
||||
* 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);
|
||||
@ -81,3 +100,49 @@ 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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user