#include "cell.h" #include #include #include "stb_image_common.h" // TGA decoding JSValue js_tga_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 TGA data from blob"); int n, width, height; void *data = stbi_load_from_memory(raw, len, &width, &height, &n, 4); if (!data) return JS_ThrowReferenceError(js, "failed to decode TGA: %s", stbi_failure_reason()); if (width <= 0 || height <= 0) { stbi_image_free(data); return JS_ThrowReferenceError(js, "decoded TGA has invalid size: %dx%d", width, height); } int pitch = width * 4; size_t pixels_size = pitch * height; JSValue obj = JS_NewObject(js); JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, width)); JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, height)); JS_SetPropertyStr(js, obj, "format", JS_NewString(js, "rgba32")); JS_SetPropertyStr(js, obj, "pitch", JS_NewInt32(js, pitch)); JS_SetPropertyStr(js, obj, "pixels", js_new_blob_stoned_copy(js, data, pixels_size)); JS_SetPropertyStr(js, obj, "depth", JS_NewInt32(js, 8)); JS_SetPropertyStr(js, obj, "hdr", JS_NewBool(js, 0)); stbi_image_free(data); return obj; } // TGA encoding JSValue js_tga_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *argv) { if (argc < 1) return JS_ThrowTypeError(js, "tga.encode requires an image object"); JSValue width_val = JS_GetPropertyStr(js, argv[0], "width"); JSValue height_val = JS_GetPropertyStr(js, argv[0], "height"); if (JS_IsNull(width_val) || JS_IsNull(height_val)) { JS_FreeValue(js, width_val); JS_FreeValue(js, height_val); return JS_ThrowTypeError(js, "tga.encode requires width and height properties"); } int width, height; if (JS_ToInt32(js, &width, width_val) < 0 || JS_ToInt32(js, &height, height_val) < 0) { JS_FreeValue(js, width_val); JS_FreeValue(js, height_val); return JS_ThrowTypeError(js, "width and height must be numbers"); } JS_FreeValue(js, width_val); JS_FreeValue(js, height_val); if (width < 1 || height < 1) return JS_ThrowRangeError(js, "width and height must be at least 1"); JSValue pixels_val = JS_GetPropertyStr(js, argv[0], "pixels"); size_t pixel_len; void *pixel_data = js_get_blob_data(js, &pixel_len, pixels_val); JS_FreeValue(js, pixels_val); if (pixel_data == NULL) return JS_EXCEPTION; if (!pixel_data) return JS_ThrowTypeError(js, "pixels blob has no data"); size_t required_size = width * height * 4; if (pixel_len < required_size) return JS_ThrowRangeError(js, "pixels buffer too small (need %zu, got %zu)", required_size, pixel_len); stbi_mem_buf buf = {0}; stbi_write_tga_to_func(stbi_mem_write, &buf, width, height, 4, pixel_data); if (!buf.data || buf.size == 0) { if (buf.data) free(buf.data); return JS_ThrowInternalError(js, "failed to encode TGA"); } 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, "format", JS_NewString(js, "tga")); JS_SetPropertyStr(js, result, "pixels", js_new_blob_stoned_copy(js, buf.data, buf.size)); free(buf.data); return result; } static const JSCFunctionListEntry js_tga_funcs[] = { MIST_FUNC_DEF(tga, decode, 1), MIST_FUNC_DEF(tga, encode, 1) }; CELL_USE_FUNCS(js_tga_funcs)