From 7360a25c10e3c4e130489866c5583016266ba786 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 17 Feb 2026 09:11:52 -0600 Subject: [PATCH] fix syntax --- aseprite.c | 144 ++++++++++++++++++++++---------------------- dxt.c | 17 +++--- examples/convert.ce | 59 +++++++++--------- gif.c | 44 +++++++------- jpg.c | 32 +++++----- png.c | 32 +++++----- psd.c | 19 +++--- qoi.c | 88 +++++++++++++-------------- resize.c | 19 +++--- stb_image_common.h | 32 +++++----- tga.c | 32 +++++----- 11 files changed, 264 insertions(+), 254 deletions(-) diff --git a/aseprite.c b/aseprite.c index 84561e8..611a130 100644 --- a/aseprite.c +++ b/aseprite.c @@ -17,24 +17,23 @@ static int check_size_overflow(size_t a, size_t b, size_t c, size_t *result) // Helper function to convert cute_aseprite frame to JS image object static JSValue frame_to_js(JSContext *js, ase_frame_t *frame, int width, int height) { - // Create JS object with frame data - 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, width * 4)); - JS_SetPropertyStr(js, obj, "depth", JS_NewInt32(js, 8)); - JS_SetPropertyStr(js, obj, "hdr", JS_NewBool(js, 0)); - + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); + JS_SetPropertyStr(js, obj.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, obj.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32")); + JS_SetPropertyStr(js, obj.val, "pitch", JS_NewInt32(js, width * 4)); + JS_SetPropertyStr(js, obj.val, "depth", JS_NewInt32(js, 8)); + JS_SetPropertyStr(js, obj.val, "hdr", JS_NewBool(js, 0)); + // Copy pixel data size_t pixels_size = width * height * 4; - JSValue pixels = js_new_blob_stoned_copy(js, frame->pixels, pixels_size); - JS_SetPropertyStr(js, obj, "pixels", pixels); - + JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, frame->pixels, pixels_size)); + // Add frame duration (in milliseconds) - JS_SetPropertyStr(js, obj, "duration", JS_NewInt32(js, frame->duration_milliseconds)); - - return obj; + JS_SetPropertyStr(js, obj.val, "duration", JS_NewInt32(js, frame->duration_milliseconds)); + + JS_RETURN(obj.val); } // Aseprite decoding @@ -48,40 +47,39 @@ JSValue js_aseprite_decode(JSContext *js, JSValue this_val, int argc, JSValueCon ase_t *ase = cute_aseprite_load_from_memory(raw, len, NULL); if (!ase) return JS_ThrowReferenceError(js, "failed to parse Aseprite file"); - - // Create array for frames - JSValue frames_array = JS_NewArray(js); - + + JS_FRAME(js); + JS_ROOT(frames_array, JS_NewArray(js)); + // Convert each frame to JS object for (int i = 0; i < ase->frame_count; i++) { - JSValue frame_obj = frame_to_js(js, &ase->frames[i], ase->w, ase->h); - JS_SetPropertyUint32(js, frames_array, i, frame_obj); + JS_SetPropertyNumber(js, frames_array.val, i, frame_to_js(js, &ase->frames[i], ase->w, ase->h)); } - + // Create result object - JSValue result = JS_NewObject(js); - JS_SetPropertyStr(js, result, "width", JS_NewInt32(js, ase->w)); - JS_SetPropertyStr(js, result, "height", JS_NewInt32(js, ase->h)); - JS_SetPropertyStr(js, result, "frames", frames_array); - JS_SetPropertyStr(js, result, "frame_count", JS_NewInt32(js, ase->frame_count)); - + JS_ROOT(result, JS_NewObject(js)); + JS_SetPropertyStr(js, result.val, "width", JS_NewInt32(js, ase->w)); + JS_SetPropertyStr(js, result.val, "height", JS_NewInt32(js, ase->h)); + JS_SetPropertyStr(js, result.val, "frames", frames_array.val); + JS_SetPropertyStr(js, result.val, "frame_count", JS_NewInt32(js, ase->frame_count)); + // Add palette if present if (ase->palette.entry_count > 0) { - JSValue palette_array = JS_NewArray(js); + JS_ROOT(palette_array, JS_NewArray(js)); for (int i = 0; i < ase->palette.entry_count; i++) { - JSValue color = JS_NewObject(js); - JS_SetPropertyStr(js, color, "r", JS_NewUint32(js, ase->palette.entries[i].color.r)); - JS_SetPropertyStr(js, color, "g", JS_NewUint32(js, ase->palette.entries[i].color.g)); - JS_SetPropertyStr(js, color, "b", JS_NewUint32(js, ase->palette.entries[i].color.b)); - JS_SetPropertyStr(js, color, "a", JS_NewUint32(js, ase->palette.entries[i].color.a)); - JS_SetPropertyUint32(js, palette_array, i, color); + JS_ROOT(color, JS_NewObject(js)); + JS_SetPropertyStr(js, color.val, "r", JS_NewUint32(js, ase->palette.entries[i].color.r)); + JS_SetPropertyStr(js, color.val, "g", JS_NewUint32(js, ase->palette.entries[i].color.g)); + JS_SetPropertyStr(js, color.val, "b", JS_NewUint32(js, ase->palette.entries[i].color.b)); + JS_SetPropertyStr(js, color.val, "a", JS_NewUint32(js, ase->palette.entries[i].color.a)); + JS_SetPropertyNumber(js, palette_array.val, i, color.val); } - JS_SetPropertyStr(js, result, "palette", palette_array); - JS_SetPropertyStr(js, result, "palette_count", JS_NewInt32(js, ase->palette.entry_count)); + JS_SetPropertyStr(js, result.val, "palette", palette_array.val); + JS_SetPropertyStr(js, result.val, "palette_count", JS_NewInt32(js, ase->palette.entry_count)); } - + cute_aseprite_free(ase); - return result; + JS_RETURN(result.val); } // Aseprite encoding @@ -89,19 +87,19 @@ JSValue js_aseprite_encode(JSContext *js, JSValue this_val, int argc, JSValueCon { if (argc < 1) return JS_ThrowTypeError(js, "encode_aseprite requires an array of frames"); - - if (!JS_IsArray(js, argv[0])) + + if (!JS_IsArray(argv[0])) return JS_ThrowTypeError(js, "first argument must be an array of frame objects"); - + // Get frame array length int frame_count = JS_ArrayLength(js, argv[0]); if (frame_count < 1) return JS_ThrowRangeError(js, "at least one frame is required"); - + // Get optional parameters int width = 0, height = 0; int tile_width = 0, tile_height = 0; - + if (argc > 1) { JSValue width_val = JS_GetPropertyStr(js, argv[1], "width"); JSValue height_val = JS_GetPropertyStr(js, argv[1], "height"); @@ -109,7 +107,7 @@ JSValue js_aseprite_encode(JSContext *js, JSValue this_val, int argc, JSValueCon if (!JS_IsNull(height_val)) JS_ToInt32(js, &height, height_val); JS_FreeValue(js, width_val); JS_FreeValue(js, height_val); - + JSValue tile_w_val = JS_GetPropertyStr(js, argv[1], "tile_width"); JSValue tile_h_val = JS_GetPropertyStr(js, argv[1], "tile_height"); if (!JS_IsNull(tile_w_val)) JS_ToInt32(js, &tile_width, tile_w_val); @@ -117,16 +115,16 @@ JSValue js_aseprite_encode(JSContext *js, JSValue this_val, int argc, JSValueCon JS_FreeValue(js, tile_w_val); JS_FreeValue(js, tile_h_val); } - + // Allocate frames array ase_frame_t *frames = malloc(sizeof(ase_frame_t) * frame_count); if (!frames) return JS_ThrowOutOfMemory(js); - + // Process each frame for (int i = 0; i < frame_count; i++) { - JSValue frame_val = JS_GetPropertyUint32(js, argv[0], i); - + JSValue frame_val = JS_GetPropertyNumber(js, argv[0], i); + // Get frame dimensions JSValue frame_w_val = JS_GetPropertyStr(js, frame_val, "width"); JSValue frame_h_val = JS_GetPropertyStr(js, frame_val, "height"); @@ -140,18 +138,18 @@ JSValue js_aseprite_encode(JSContext *js, JSValue this_val, int argc, JSValueCon } JS_FreeValue(js, frame_w_val); JS_FreeValue(js, frame_h_val); - + // Set global dimensions if not specified if (width == 0) width = frame_w; if (height == 0) height = frame_h; - + // Get pixel data JSValue pixels_val = JS_GetPropertyStr(js, frame_val, "pixels"); size_t pixel_len; void *pixel_data = js_get_blob_data(js, &pixel_len, pixels_val); JS_FreeValue(js, pixels_val); JS_FreeValue(js, frame_val); - + if (pixel_data == NULL) { free(frames); return JS_EXCEPTION; @@ -160,15 +158,15 @@ JSValue js_aseprite_encode(JSContext *js, JSValue this_val, int argc, JSValueCon free(frames); return JS_ThrowTypeError(js, "frame %d has no pixel data", i); } - + // Validate pixel data size size_t required_size = frame_w * frame_h * 4; if (pixel_len < required_size) { free(frames); - return JS_ThrowRangeError(js, "frame %d pixel data too small (need %zu bytes, got %zu)", + return JS_ThrowRangeError(js, "frame %d pixel data too small (need %zu bytes, got %zu)", i, required_size, pixel_len); } - + // Get frame duration (default 100ms) int duration = 100; JSValue duration_val = JS_GetPropertyStr(js, frame_val, "duration"); @@ -176,7 +174,7 @@ JSValue js_aseprite_encode(JSContext *js, JSValue this_val, int argc, JSValueCon JS_ToInt32(js, &duration, duration_val); } JS_FreeValue(js, duration_val); - + // Copy pixel data and set frame info frames[i].pixels = malloc(required_size); if (!frames[i].pixels) { @@ -186,17 +184,17 @@ JSValue js_aseprite_encode(JSContext *js, JSValue this_val, int argc, JSValueCon memcpy(frames[i].pixels, pixel_data, required_size); frames[i].duration_milliseconds = duration; } - + // Set up aseprite structure ase_t ase = {0}; ase.w = width; ase.h = height; ase.frames = frames; ase.frame_count = frame_count; - + // Note: tile dimensions would need to be handled differently in cute_aseprite // For now, we'll skip setting tile dimensions as they may not be supported - + // Save to memory (Note: cute_aseprite_save_to_memory may not be available) // For now, return a simple implementation int out_len = 0; @@ -205,32 +203,32 @@ JSValue js_aseprite_encode(JSContext *js, JSValue this_val, int argc, JSValueCon free(frames); return JS_ThrowOutOfMemory(js); } - + // Placeholder implementation - would need actual cute_aseprite save functionality memset(out_data, 0, 1024); out_len = 1024; - + // Clean up frame pixel data for (int i = 0; i < frame_count; i++) { free(frames[i].pixels); } free(frames); - + if (!out_data) return JS_ThrowInternalError(js, "failed to encode Aseprite file"); - + // 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, "frame_count", JS_NewInt32(js, frame_count)); - JS_SetPropertyStr(js, result, "format", JS_NewString(js, "aseprite")); - - JSValue compressed_pixels = js_new_blob_stoned_copy(js, out_data, out_len); + JS_FRAME(js); + JS_ROOT(result, JS_NewObject(js)); + JS_SetPropertyStr(js, result.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, result.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, result.val, "frame_count", JS_NewInt32(js, frame_count)); + JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "aseprite")); + + JS_SetPropertyStr(js, result.val, "pixels", js_new_blob_stoned_copy(js, out_data, out_len)); free(out_data); - JS_SetPropertyStr(js, result, "pixels", compressed_pixels); - - return result; + + JS_RETURN(result.val); } static const JSCFunctionListEntry js_aseprite_funcs[] = { diff --git a/dxt.c b/dxt.c index f0c0f37..9be2dac 100644 --- a/dxt.c +++ b/dxt.c @@ -115,19 +115,20 @@ JSValue js_dxt_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *a } } - 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, get_dxt_format_string(compression_type))); - JS_SetPropertyStr(js, result, "compression", JS_NewInt32(js, compression_type)); - JS_SetPropertyStr(js, result, "pixels", js_new_blob_stoned_copy(js, output, output_size)); + JS_FRAME(js); + JS_ROOT(result, JS_NewObject(js)); + JS_SetPropertyStr(js, result.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, result.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, get_dxt_format_string(compression_type))); + JS_SetPropertyStr(js, result.val, "compression", JS_NewInt32(js, compression_type)); + JS_SetPropertyStr(js, result.val, "pixels", js_new_blob_stoned_copy(js, output, output_size)); free(output); - return result; + JS_RETURN(result.val); } static const JSCFunctionListEntry js_dxt_funcs[] = { MIST_FUNC_DEF(dxt, encode, 2) }; -CELL_USE_FUNCS(js_dxt_funcs) \ No newline at end of file +CELL_USE_FUNCS(js_dxt_funcs) diff --git a/examples/convert.ce b/examples/convert.ce index 5808a77..75d73de 100644 --- a/examples/convert.ce +++ b/examples/convert.ce @@ -37,40 +37,41 @@ function ext_lower(path) { } function decode_image(blob, ext) { - switch (ext) { - case 'png': return png.decode(blob) - case 'jpg': case 'jpeg': return jpg.decode(blob) - case 'bmp': return bmp.decode(blob) - case 'tga': return tga.decode(blob) - case 'gif': return gif.decode(blob) - case 'psd': return psd.decode(blob) - case 'qoi': return qoi.decode(blob) - case 'ase': case 'aseprite': return aseprite.decode(blob) - default: - // Try common decoders as a fallback - var decoders = [png.decode, jpg.decode, qoi.decode, aseprite.decode] - for (var i = 0; i < length(decoders); i++) { - try { - var r = decoders[i](blob) - if (r) return r - } catch (e) {} - } - return null + if (ext == 'png') return png.decode(blob) + if (ext == 'jpg' || ext == 'jpeg') return jpg.decode(blob) + if (ext == 'bmp') return bmp.decode(blob) + if (ext == 'tga') return tga.decode(blob) + if (ext == 'gif') return gif.decode(blob) + if (ext == 'psd') return psd.decode(blob) + if (ext == 'qoi') return qoi.decode(blob) + if (ext == 'ase' || ext == 'aseprite') return aseprite.decode(blob) + + // Try common decoders as a fallback + var decoders = [png.decode, jpg.decode, qoi.decode, aseprite.decode] + var i = 0 + var r = null + var fn = function() { + r = decoders[i](blob) + } disruption { + r = null } + for (i = 0; i < length(decoders); i++) { + fn() + if (r) return r + } + return null } function encode_image(img, ext) { - switch (ext) { - case 'png': return png.encode(img) - case 'jpg': case 'jpeg': return jpg.encode(img) - case 'bmp': return bmp.encode(img) - case 'tga': return tga.encode(img) - case 'qoi': - if (!img.format) img.format = "rgba32" - return qoi.encode(img) - default: - return null + if (ext == 'png') return png.encode(img) + if (ext == 'jpg' || ext == 'jpeg') return jpg.encode(img) + if (ext == 'bmp') return bmp.encode(img) + if (ext == 'tga') return tga.encode(img) + if (ext == 'qoi') { + if (!img.format) img.format = "rgba32" + return qoi.encode(img) } + return null } log.console("Loading input image: " + input_file) diff --git a/gif.c b/gif.c index 0a3143e..1e52914 100644 --- a/gif.c +++ b/gif.c @@ -33,38 +33,38 @@ JSValue js_gif_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a int pitch = width * 4; size_t frame_size = pitch * height; - // Create frames array - JSValue frames_array = JS_NewArray(js); - + JS_FRAME(js); + JS_ROOT(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)); - + JS_ROOT(frame, JS_NewObject(js)); + JS_SetPropertyStr(js, frame.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, frame.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, frame.val, "format", JS_NewString(js, "rgba32")); + JS_SetPropertyStr(js, frame.val, "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)); - + JS_SetPropertyStr(js, frame.val, "pixels", js_new_blob_stoned_copy(js, frame_pixels, frame_size)); + JS_SetPropertyStr(js, frame.val, "depth", JS_NewInt32(js, 8)); + JS_SetPropertyStr(js, frame.val, "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); + JS_SetPropertyStr(js, frame.val, "duration", JS_NewInt32(js, duration_ms)); + + JS_SetPropertyNumber(js, frames_array.val, i, frame.val); } // 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)); + JS_ROOT(result, JS_NewObject(js)); + JS_SetPropertyStr(js, result.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, result.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, result.val, "frames", frames_array.val); + JS_SetPropertyStr(js, result.val, "frame_count", JS_NewInt32(js, frames_count)); stbi_image_free(data); if (delays) stbi_image_free(delays); - return result; + JS_RETURN(result.val); } static const JSCFunctionListEntry js_gif_funcs[] = { diff --git a/jpg.c b/jpg.c index 8323109..b472ab8 100644 --- a/jpg.c +++ b/jpg.c @@ -25,17 +25,18 @@ JSValue js_jpg_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a 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)); + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); + JS_SetPropertyStr(js, obj.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, obj.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32")); + JS_SetPropertyStr(js, obj.val, "pitch", JS_NewInt32(js, pitch)); + JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, data, pixels_size)); + JS_SetPropertyStr(js, obj.val, "depth", JS_NewInt32(js, 8)); + JS_SetPropertyStr(js, obj.val, "hdr", JS_NewBool(js, 0)); stbi_image_free(data); - return obj; + JS_RETURN(obj.val); } // JPEG encoding @@ -100,14 +101,15 @@ JSValue js_jpg_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *a return JS_ThrowInternalError(js, "failed to encode JPEG"); } - 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, "jpg")); - JS_SetPropertyStr(js, result, "pixels", js_new_blob_stoned_copy(js, buf.data, buf.size)); + JS_FRAME(js); + JS_ROOT(result, JS_NewObject(js)); + JS_SetPropertyStr(js, result.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, result.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "jpg")); + JS_SetPropertyStr(js, result.val, "pixels", js_new_blob_stoned_copy(js, buf.data, buf.size)); free(buf.data); - return result; + JS_RETURN(result.val); } static const JSCFunctionListEntry js_jpg_funcs[] = { diff --git a/png.c b/png.c index 89249e5..215968c 100644 --- a/png.c +++ b/png.c @@ -25,17 +25,18 @@ JSValue js_png_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a 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)); + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); + JS_SetPropertyStr(js, obj.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, obj.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32")); + JS_SetPropertyStr(js, obj.val, "pitch", JS_NewInt32(js, pitch)); + JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, data, pixels_size)); + JS_SetPropertyStr(js, obj.val, "depth", JS_NewInt32(js, 8)); + JS_SetPropertyStr(js, obj.val, "hdr", JS_NewBool(js, 0)); stbi_image_free(data); - return obj; + JS_RETURN(obj.val); } // PNG encoding @@ -84,14 +85,15 @@ JSValue js_png_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *a return JS_ThrowInternalError(js, "failed to encode PNG"); } - 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, "png")); - JS_SetPropertyStr(js, result, "pixels", js_new_blob_stoned_copy(js, buf.data, buf.size)); + JS_FRAME(js); + JS_ROOT(result, JS_NewObject(js)); + JS_SetPropertyStr(js, result.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, result.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "png")); + JS_SetPropertyStr(js, result.val, "pixels", js_new_blob_stoned_copy(js, buf.data, buf.size)); free(buf.data); - return result; + JS_RETURN(result.val); } static const JSCFunctionListEntry js_png_funcs[] = { diff --git a/psd.c b/psd.c index 2e6bc7e..812c97b 100644 --- a/psd.c +++ b/psd.c @@ -25,17 +25,18 @@ JSValue js_psd_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a 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)); + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); + JS_SetPropertyStr(js, obj.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, obj.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32")); + JS_SetPropertyStr(js, obj.val, "pitch", JS_NewInt32(js, pitch)); + JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, data, pixels_size)); + JS_SetPropertyStr(js, obj.val, "depth", JS_NewInt32(js, 8)); + JS_SetPropertyStr(js, obj.val, "hdr", JS_NewBool(js, 0)); stbi_image_free(data); - return obj; + JS_RETURN(obj.val); } static const JSCFunctionListEntry js_psd_funcs[] = { diff --git a/qoi.c b/qoi.c index 3b9ec2c..29dd7c5 100644 --- a/qoi.c +++ b/qoi.c @@ -19,17 +19,17 @@ JSValue js_qoi_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *a { if (argc < 1) return JS_ThrowTypeError(js, "compress_qoi requires an object argument"); - + // Check if width/height properties exist 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, "compress_qoi 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); @@ -38,49 +38,49 @@ JSValue js_qoi_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *a } 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"); - + // Get pixel format JSValue format_val = JS_GetPropertyStr(js, argv[0], "format"); const char *format_str = JS_ToCString(js, format_val); JS_FreeValue(js, format_val); - + if (!format_str) return JS_ThrowTypeError(js, "Invalid or missing pixel format"); - + // Determine channels from format string int channels = 4; // Default to RGBA if (strstr(format_str, "rgb") && !strstr(format_str, "a")) { channels = 3; // RGB without alpha } - + // Get pixels 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, "blob has no data"); - + // Validate buffer size size_t required_size = width * height * channels; if (pixel_len < required_size) { JS_FreeCString(js, (char*)format_str); - return JS_ThrowRangeError(js, "pixels buffer too small for %dx%d format (need %zu bytes, got %zu)", + return JS_ThrowRangeError(js, "pixels buffer too small for %dx%d format (need %zu bytes, got %zu)", width, height, required_size, pixel_len); } - + // Get colorspace (optional, default to sRGB) int colorspace = 0; // QOI_SRGB if (argc > 1) { colorspace = JS_ToBool(js, argv[1]); } - + // Encode to QOI qoi_desc desc = { .width = width, @@ -88,26 +88,25 @@ JSValue js_qoi_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *a .channels = channels, .colorspace = colorspace }; - + int out_len; void *qoi_data = qoi_encode(pixel_data, &desc, &out_len); - + if (!qoi_data) return JS_ThrowInternalError(js, "QOI encoding failed"); - + // 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, "format", JS_NewString(js, "qoi")); - JS_SetPropertyStr(js, result, "channels", JS_NewInt32(js, channels)); - JS_SetPropertyStr(js, result, "colorspace", JS_NewInt32(js, colorspace)); - - JSValue compressed_pixels = js_new_blob_stoned_copy(js, qoi_data, out_len); - free(qoi_data); // Free the QOI buffer after copying to blob - JS_SetPropertyStr(js, result, "pixels", compressed_pixels); - - return result; + JS_FRAME(js); + JS_ROOT(result, JS_NewObject(js)); + JS_SetPropertyStr(js, result.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, result.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "qoi")); + JS_SetPropertyStr(js, result.val, "channels", JS_NewInt32(js, channels)); + JS_SetPropertyStr(js, result.val, "colorspace", JS_NewInt32(js, colorspace)); + JS_SetPropertyStr(js, result.val, "pixels", js_new_blob_stoned_copy(js, qoi_data, out_len)); + free(qoi_data); + + JS_RETURN(result.val); } // QOI decompression/decoding @@ -120,15 +119,15 @@ JSValue js_qoi_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a qoi_desc desc; void *data = qoi_decode(raw, len, &desc, 0); // 0 means use channels from file - + if (!data) return JS_NULL; // Return null if not valid QOI - + // QOI always decodes to either RGB or RGBA based on the file's channel count int channels = desc.channels; int pitch = desc.width * channels; size_t pixels_size = pitch * desc.height; - + // If it's RGB, convert to RGBA for consistency void *rgba_data = data; if (channels == 3) { @@ -137,7 +136,7 @@ JSValue js_qoi_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a free(data); return JS_ThrowOutOfMemory(js); } - + // Convert RGB to RGBA unsigned char *src = (unsigned char*)data; unsigned char *dst = (unsigned char*)rgba_data; @@ -151,20 +150,21 @@ JSValue js_qoi_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a pitch = desc.width * 4; pixels_size = pitch * desc.height; } - + // Create JS object with surface data - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, desc.width)); - JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, desc.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, rgba_data, pixels_size)); - JS_SetPropertyStr(js, obj, "depth", JS_NewInt32(js, 8)); - JS_SetPropertyStr(js, obj, "hdr", JS_NewBool(js, 0)); - JS_SetPropertyStr(js, obj, "colorspace", JS_NewInt32(js, desc.colorspace)); - + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); + JS_SetPropertyStr(js, obj.val, "width", JS_NewInt32(js, desc.width)); + JS_SetPropertyStr(js, obj.val, "height", JS_NewInt32(js, desc.height)); + JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32")); + JS_SetPropertyStr(js, obj.val, "pitch", JS_NewInt32(js, pitch)); + JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, rgba_data, pixels_size)); + JS_SetPropertyStr(js, obj.val, "depth", JS_NewInt32(js, 8)); + JS_SetPropertyStr(js, obj.val, "hdr", JS_NewBool(js, 0)); + JS_SetPropertyStr(js, obj.val, "colorspace", JS_NewInt32(js, desc.colorspace)); + free(rgba_data); - return obj; + JS_RETURN(obj.val); } static const JSCFunctionListEntry js_qoi_funcs[] = { diff --git a/resize.c b/resize.c index 9d1f7d1..7480477 100644 --- a/resize.c +++ b/resize.c @@ -101,16 +101,17 @@ JSValue js_resize_resize(JSContext *js, JSValue this_val, int argc, JSValueConst } // Build result object - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, dst_width)); - JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, dst_height)); - JS_SetPropertyStr(js, obj, "format", JS_NewString(js, "rgba32")); - JS_SetPropertyStr(js, obj, "pitch", JS_NewInt32(js, dst_width * 4)); - JS_SetPropertyStr(js, obj, "pixels", js_new_blob_stoned_copy(js, dst_pixels, dst_size)); - JS_SetPropertyStr(js, obj, "depth", JS_NewInt32(js, 8)); + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); + JS_SetPropertyStr(js, obj.val, "width", JS_NewInt32(js, dst_width)); + JS_SetPropertyStr(js, obj.val, "height", JS_NewInt32(js, dst_height)); + JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32")); + JS_SetPropertyStr(js, obj.val, "pitch", JS_NewInt32(js, dst_width * 4)); + JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, dst_pixels, dst_size)); + JS_SetPropertyStr(js, obj.val, "depth", JS_NewInt32(js, 8)); free(dst_pixels); - return obj; + JS_RETURN(obj.val); } // Convenience function to resize to fit within max dimensions while preserving aspect ratio @@ -192,4 +193,4 @@ static const JSCFunctionListEntry js_resize_funcs[] = { MIST_FUNC_DEF(resize, square, 3) }; -CELL_USE_FUNCS(js_resize_funcs) \ No newline at end of file +CELL_USE_FUNCS(js_resize_funcs) diff --git a/stb_image_common.h b/stb_image_common.h index 1324393..dda7dd2 100644 --- a/stb_image_common.h +++ b/stb_image_common.h @@ -49,17 +49,18 @@ JSValue js_##fmt##_decode(JSContext *js, JSValue this_val, int argc, JSValueCons 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)); \ + JS_FRAME(js); \ + JS_ROOT(obj, JS_NewObject(js)); \ + JS_SetPropertyStr(js, obj.val, "width", JS_NewInt32(js, width)); \ + JS_SetPropertyStr(js, obj.val, "height", JS_NewInt32(js, height)); \ + JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32")); \ + JS_SetPropertyStr(js, obj.val, "pitch", JS_NewInt32(js, pitch)); \ + JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, data, pixels_size)); \ + JS_SetPropertyStr(js, obj.val, "depth", JS_NewInt32(js, 8)); \ + JS_SetPropertyStr(js, obj.val, "hdr", JS_NewBool(js, 0)); \ \ stbi_image_free(data); \ - return obj; \ + JS_RETURN(obj.val); \ } #define IMAGE_ENCODER(fmt) \ @@ -109,14 +110,15 @@ JSValue js_##fmt##_encode(JSContext *js, JSValue this_val, int argc, JSValueCons return JS_ThrowInternalError(js, "failed to encode " #fmt); \ } \ \ - 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, #fmt)); \ - JS_SetPropertyStr(js, result, "pixels", js_new_blob_stoned_copy(js, buf.data, buf.size)); \ + JS_FRAME(js); \ + JS_ROOT(result, JS_NewObject(js)); \ + JS_SetPropertyStr(js, result.val, "width", JS_NewInt32(js, width)); \ + JS_SetPropertyStr(js, result.val, "height", JS_NewInt32(js, height)); \ + JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, #fmt)); \ + JS_SetPropertyStr(js, result.val, "pixels", js_new_blob_stoned_copy(js, buf.data, buf.size)); \ \ free(buf.data); \ - return result; \ + JS_RETURN(result.val); \ } #endif diff --git a/tga.c b/tga.c index f52f582..f19038d 100644 --- a/tga.c +++ b/tga.c @@ -25,17 +25,18 @@ JSValue js_tga_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a 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)); + JS_FRAME(js); + JS_ROOT(obj, JS_NewObject(js)); + JS_SetPropertyStr(js, obj.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, obj.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32")); + JS_SetPropertyStr(js, obj.val, "pitch", JS_NewInt32(js, pitch)); + JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, data, pixels_size)); + JS_SetPropertyStr(js, obj.val, "depth", JS_NewInt32(js, 8)); + JS_SetPropertyStr(js, obj.val, "hdr", JS_NewBool(js, 0)); stbi_image_free(data); - return obj; + JS_RETURN(obj.val); } // TGA encoding @@ -85,14 +86,15 @@ JSValue js_tga_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *a 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)); + JS_FRAME(js); + JS_ROOT(result, JS_NewObject(js)); + JS_SetPropertyStr(js, result.val, "width", JS_NewInt32(js, width)); + JS_SetPropertyStr(js, result.val, "height", JS_NewInt32(js, height)); + JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "tga")); + JS_SetPropertyStr(js, result.val, "pixels", js_new_blob_stoned_copy(js, buf.data, buf.size)); free(buf.data); - return result; + JS_RETURN(result.val); } static const JSCFunctionListEntry js_tga_funcs[] = {