Commit9a4935d0Recorded12 Jul 2026Repositorysigil-graphics
graphics/image: add load-image-from-memory (decode encoded bytes) for web
Message
On the web there is no filesystem, so load-image's stbiload(path) can't be used. Add (load-image-from-memory ptr len): decode an image from raw encoded bytes already resident in linear memory (a wasm address + length), via stbimage's stbiloadfrom_memory, producing the same <image> object load-image returns from a path. The web build's sigil-wasm-gles3 app-shell fetches the file and copies its bytes into memory; this turns them into a GPU texture.
Purely ADDITIVE: a new procedure on native + web alike (stbiloadfrommemory is core stbimage). It changes no existing behaviour and removes no lines; it is exported unconditionally because the module's (export) list must match the C registration on both platforms. Native image.c compiles unchanged.
Changed
src/c/image.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/sigil/graphics/image.sgl | 1 +
2 files changed, 53 insertions(+)Diff
src/c/image.cmodified
@@ -98,6 +98,55 @@ static Value native_load_image(SigilVM *vm, int argc, Value *args)
98
sizeof(StudioImage) + (size_t)(width * height * 4)); 99
} 100
+101
/*+102
* (load-image-from-memory ptr len) -> <image> or #f+103
*+104
* Decode an image from raw ENCODED bytes already resident in WASM linear memory+105
* at address `ptr` (a fixnum), `len` bytes long, using stb_image's from-memory+106
* decoder. On web there is no filesystem: JS fetches the image file and copies+107
* the encoded bytes into memory (see the sigil-wasm-gles3 app-shell), and this+108
* turns them into the same <image> object load-image produces from a path.+109
*+110
* ADDITIVE: a new procedure available on native + web alike. It changes no+111
* existing behaviour (stbi_load_from_memory is core stb_image); it is exported+112
* unconditionally because the module's (export) list must match the C+113
* registration on both platforms.+114
*/+115
static Value native_load_image_from_memory(SigilVM *vm, int argc, Value *args)+116
{+117
(void)argc;+118
+119
if (!sigil_is_fixnum(args[0]) || !sigil_is_fixnum(args[1])) {+120
sigil__vm_set_error(vm, SIGIL_ERR_TYPE,+121
"load-image-from-memory: expected (ptr len) fixnums");+122
return SIGIL_FALSE;+123
}+124
+125
const unsigned char *bytes = (const unsigned char *)(uintptr_t)sigil_as_fixnum(args[0]);+126
int len = (int)sigil_as_fixnum(args[1]);+127
if (!bytes || len <= 0) return SIGIL_FALSE;+128
+129
int width, height, channels;+130
unsigned char *pixels = stbi_load_from_memory(bytes, len, &width, &height, &channels, 4);+131
if (!pixels) return SIGIL_FALSE;+132
+133
StudioImage *img = malloc(sizeof(StudioImage));+134
if (!img) {+135
stbi_image_free(pixels);+136
sigil__vm_set_error(vm, SIGIL_ERR_MEMORY, "load-image-from-memory: out of memory");+137
return SIGIL_FALSE;+138
}+139
+140
img->pixels = pixels;+141
img->width = width;+142
img->height = height;+143
img->channels = 4;+144
+145
ensure_image_type(vm);+146
return sigil_make_foreign(vm, image_type_tag, img, image_destructor,+147
sizeof(StudioImage) + (size_t)(width * height * 4));+148
}+149
150
/* 151
* (image? obj) -> boolean 152
*@@ -214,6 +263,8 @@ void sigil__init_sigil_graphics_image_module(SigilVM *vm)
263
264
sigil_module_register_native(vm, "load-image", native_load_image, 265
SIGIL_ARITY_EXACT(1), "Load image from file path");+266
sigil_module_register_native(vm, "load-image-from-memory", native_load_image_from_memory,+267
SIGIL_ARITY_EXACT(2), "Decode an image from encoded bytes in memory"); 268
sigil_module_register_native(vm, "image?", native_image_p, 269
SIGIL_ARITY_EXACT(1), "Check if object is an image"); 270
sigil_module_register_native(vm, "image-width", native_image_width,@@ -226,6 +277,7 @@ void sigil__init_sigil_graphics_image_module(SigilVM *vm)
277
SIGIL_ARITY_EXACT(1), "Free image pixel data"); 278
279
sigil_module_export(vm, "load-image");+280
sigil_module_export(vm, "load-image-from-memory"); 281
sigil_module_export(vm, "image?"); 282
sigil_module_export(vm, "image-width"); 283
sigil_module_export(vm, "image-height");src/sigil/graphics/image.sglmodified
@@ -13,6 +13,7 @@
13
(export 14
;; Image loading 15
load-image+16
load-image-from-memory 17
18
;; Image predicates and properties 19
image?