#include "cell.h" #include #include #include "stb_image_common.h" // GIF decoding via stb_image - handles animated GIFs // Returns an object with: // .width, .height - dimensions // .frames - array of frame objects, each with: // .width, .height, .format, .pitch, .pixels, .depth, .hdr // .duration - frame duration in milliseconds (for animated GIFs) // .frame_count - number of frames JSValue js_gif_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *argv) { size_t len; void *raw = js_get_blob_data(js, &len, argv[0]); if (raw == NULL) return JS_EXCEPTION; if (!raw) return JS_ThrowReferenceError(js, "could not get GIF data from blob"); int n, width, height, frames_count; int *delays = NULL; void *data = stbi_load_gif_from_memory(raw, len, &delays, &width, &height, &frames_count, &n, 4); if (!data) return JS_ThrowReferenceError(js, "failed to decode GIF: %s", stbi_failure_reason()); if (width <= 0 || height <= 0) { stbi_image_free(data); if (delays) stbi_image_free(delays); return JS_ThrowReferenceError(js, "decoded GIF has invalid size: %dx%d", width, height); } int pitch = width * 4; size_t frame_size = pitch * height; // Create frames array JSValue frames_array = JS_NewArray(js); for (int i = 0; i < frames_count; i++) { JSValue frame = JS_NewObject(js); JS_SetPropertyStr(js, frame, "width", JS_NewInt32(js, width)); JS_SetPropertyStr(js, frame, "height", JS_NewInt32(js, height)); JS_SetPropertyStr(js, frame, "format", JS_NewString(js, "rgba32")); JS_SetPropertyStr(js, frame, "pitch", JS_NewInt32(js, pitch)); void *frame_pixels = (unsigned char*)data + (frame_size * i); JS_SetPropertyStr(js, frame, "pixels", js_new_blob_stoned_copy(js, frame_pixels, frame_size)); JS_SetPropertyStr(js, frame, "depth", JS_NewInt32(js, 8)); JS_SetPropertyStr(js, frame, "hdr", JS_NewBool(js, 0)); // Add duration in milliseconds (delays are in centiseconds, i.e. 1/100th of a second) int duration_ms = delays ? delays[i] * 10 : 100; JS_SetPropertyStr(js, frame, "duration", JS_NewInt32(js, duration_ms)); JS_SetPropertyUint32(js, frames_array, i, frame); } // Create result object JSValue result = JS_NewObject(js); JS_SetPropertyStr(js, result, "width", JS_NewInt32(js, width)); JS_SetPropertyStr(js, result, "height", JS_NewInt32(js, height)); JS_SetPropertyStr(js, result, "frames", frames_array); JS_SetPropertyStr(js, result, "frame_count", JS_NewInt32(js, frames_count)); stbi_image_free(data); if (delays) stbi_image_free(delays); return result; } static const JSCFunctionListEntry js_gif_funcs[] = { MIST_FUNC_DEF(gif, decode, 1) }; CELL_USE_FUNCS(js_gif_funcs)