library: vendors/stb_image + vendors/stb_truetype — stb ships with sx

The stb headers move from the repo-root vendors/ (resolvable only
with CWD = sx repo) into library/vendors/ following the sqlite
convention — bindings module + c/ sources + provenance README — so
'#import "vendors/stb_image/stb_image.sx"' (image v2.30 + image_write
v1.16) and '#import "vendors/stb_truetype/stb_truetype.sx"' (v1.26)
work from any consumer via the stdlib search paths. modules/ffi/stb.sx
dissolves into the stb_image vendor; modules/ffi/stb_truetype.sx keeps
its non-stb text-shaping companions and re-imports the vendored unit.
examples/1625 pins a deterministic in-memory BMP decode; examples/1626
pins font init + metric invariants against the system Helvetica.
This commit is contained in:
agra
2026-06-12 17:50:21 +03:00
parent 2097772ec9
commit 58af806b7a
22 changed files with 163 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
#import "modules/std.sx";
stb :: #import "modules/ffi/stb.sx";
stb :: #import "vendors/stb_image/stb_image.sx";
main :: () -> i32 {
w: i32 = 0;

View File

@@ -0,0 +1,35 @@
// The sx library ships stb_image: `#import "vendors/stb_image/
// stb_image.sx"` resolves through the stdlib search paths and the
// implementation compiles through the object cache. Decodes a 2x2
// 24-bit BMP built in memory — fully deterministic: dimensions,
// channel count, and the top-left pixel (blue) are pinned.
#import "modules/std.sx";
stb :: #import "vendors/stb_image/stb_image.sx";
main :: () -> i32 {
// BITMAPFILEHEADER (14) + BITMAPINFOHEADER (40) + 2 rows of
// 2 BGR pixels padded to 4-byte rows (8 each) = 70 bytes.
// Bottom row: red, green; top row: blue, white (BMP is bottom-up).
bmp : [70]u8 = .{
66, 77, 70, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0,
40, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 1, 0, 24, 0,
0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 255, 0, 255, 0, 0, 0,
255, 0, 0, 255, 255, 255, 0, 0,
};
w : i32 = 0;
h : i32 = 0;
ch : i32 = 0;
img := stb.stbi_load_from_memory(@bmp[0], 70, @w, @h, @ch, 3);
if xx img == 0 {
print("decode failed\n");
return 1;
}
p : [*]u8 = xx img;
print("decoded {}x{} ({} channels)\n", w, h, ch);
print("top-left pixel: {} {} {}\n", p[0], p[1], p[2]);
stb.stbi_image_free(xx img);
0
}

View File

@@ -0,0 +1,36 @@
// The sx library ships stb_truetype: `#import "vendors/stb_truetype/
// stb_truetype.sx"` resolves through the stdlib search paths and the
// implementation compiles through the object cache. Loads the system
// Helvetica collection and pins INVARIANTS only (font versions vary
// across macOS releases): init succeeds, the pixel-height scale is
// positive, the ascender is positive and the descender negative.
#import "modules/std.sx";
fs :: #import "modules/std/fs.sx";
tt :: #import "vendors/stb_truetype/stb_truetype.sx";
main :: () -> i32 {
data := fs.read_file("/System/Library/Fonts/Helvetica.ttc");
if data == null {
print("font missing\n");
return 1;
}
bytes := data!;
off := tt.stbtt_GetFontOffsetForIndex(bytes.ptr, 0);
print("font offset >= 0: {}\n", off >= 0);
info : *void = xx context.allocator.alloc_bytes(256);
ok := tt.stbtt_InitFont(info, bytes.ptr, off);
print("init ok: {}\n", ok != 0);
px : f32 = 32.0;
scale := tt.stbtt_ScaleForPixelHeight(info, px);
print("scale > 0: {}\n", scale > 0.0);
ascent : i32 = 0;
descent : i32 = 0;
linegap : i32 = 0;
tt.stbtt_GetFontVMetrics(info, @ascent, @descent, @linegap);
print("ascent > 0, descent < 0: {} {}\n", ascent > 0, descent < 0);
0
}

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1,2 @@
decoded 2x2 (3 channels)
top-left pixel: 0 0 255

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1,4 @@
font offset >= 0: true
init ok: true
scale > 0: true
ascent > 0, descent < 0: true true