fix syntax

This commit is contained in:
2026-02-17 09:11:52 -06:00
parent b532083a3a
commit 7360a25c10
11 changed files with 264 additions and 254 deletions

View File

@@ -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[] = {

17
dxt.c
View File

@@ -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)
CELL_USE_FUNCS(js_dxt_funcs)

View File

@@ -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)

44
gif.c
View File

@@ -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[] = {

32
jpg.c
View File

@@ -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[] = {

32
png.c
View File

@@ -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[] = {

19
psd.c
View File

@@ -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[] = {

88
qoi.c
View File

@@ -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[] = {

View File

@@ -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)
CELL_USE_FUNCS(js_resize_funcs)

View File

@@ -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

32
tga.c
View File

@@ -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[] = {