#include "cell.h" #include "prosperon.h" #include "HandmadeMath.h" #define STB_IMAGE_IMPLEMENTATION #define STBI_FAILURE_USERMSG #define STBI_NO_STDIO #include "stb_image.h" #define STB_RECT_PACK_IMPLEMENTATION #include "stb_rect_pack.h" #define CUTE_ASEPRITE_IMPLEMENTATION #include "cute_aseprite.h" // input: (encoded image data of jpg, png, bmp, tiff) JSC_CCALL(os_image_decode, size_t len; void *raw = js_get_blob_data(js, &len, argv[0]); if (raw == -1) return JS_EXCEPTION; int n, width, height; void *data = stbi_load_from_memory(raw, len, &width, &height, &n, 4); if (!data) return JS_ThrowReferenceError(js, "no known image type from pixel data: %s", stbi_failure_reason()); if (width <= 0 || height <= 0) { free(data); return JS_ThrowReferenceError(js, "decoded image has invalid size: %dx%d", width, height); } int pitch = width*4; size_t pixels_size = pitch * height; // Create JS object with surface 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, 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)); free(data); ret = obj; ) JSC_CCALL(graphics_hsl_to_rgb, float h, s, l; JS_ToFloat64(js, &h, argv[0]); JS_ToFloat64(js, &s, argv[1]); JS_ToFloat64(js, &l, argv[2]); float c = (1 - abs(2 * l - 1)) * s; float x = c * (1 - abs(fmod((h/60),2) - 1)); float m = l - c / 2; float r = 0, g = 0, b = 0; if (h < 60) { r = c; g = x; } else if (h < 120) { r = x; g = c; } else if (h < 180) { g = c; b = x; } else if (h < 240) { g = x; b = c; } else if (h < 300) { r = x; b = c; } else { r = c; b = x; } return color2js(js, (colorf){r+m, g+m, b+m, 1}); ) JSC_CCALL(os_make_line_prim, return JS_NULL; /* JSValue prim = JS_NewObject(js); HMM_Vec2 *v = js2cpvec2arr(js,argv[0]); parsl_context *par_ctx = parsl_create_context((parsl_config){ .thickness = js2number(js,argv[1]), .flags= PARSL_FLAG_ANNOTATIONS, .u_mode = js2number(js,argv[2]) }); uint16_t spine_lens[] = {arrlen(v)}; parsl_mesh *m = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){ .num_vertices = arrlen(v), .num_spines = 1, .vertices = v, .spine_lengths = spine_lens, .closed = JS_ToBool(js,argv[3]) }); JS_SetPropertyStr(js, prim, "pos", make_gpu_buffer(js,m->positions,sizeof(*m->positions)*m->num_vertices, 0, 2,1,0)); JS_SetPropertyStr(js, prim, "indices", make_gpu_buffer(js,m->triangle_indices,sizeof(*m->triangle_indices)*m->num_triangles*3, JS_TYPED_ARRAY_UINT32, 1,1,1)); float uv[m->num_vertices*2]; for (int i = 0; i < m->num_vertices; i++) { uv[i*2] = m->annotations[i].u_along_curve; uv[i*2+1] = m->annotations[i].v_across_curve; } JS_SetPropertyStr(js, prim, "uv", make_gpu_buffer(js, uv, sizeof(uv), 0,2,1,0)); JS_SetPropertyStr(js,prim,"vertices", number2js(js,m->num_vertices)); JS_SetPropertyStr(js,prim,"num_indices", number2js(js,m->num_triangles*3)); JS_SetPropertyStr(js,prim,"first_index", number2js(js,0)); parsl_destroy_context(par_ctx); return prim; */ ) JSC_CCALL(os_rectpack, int width = js2number(js,argv[0]); int height = js2number(js,argv[1]); int num = JS_ArrayLength(js,argv[2]); stbrp_context ctx[1]; stbrp_rect rects[num]; for (int i = 0; i < num; i++) { HMM_Vec2 wh = js2vec2(js,JS_GetPropertyUint32(js, argv[2], i)); rects[i].w = wh.x; rects[i].h = wh.y; rects[i].id = i; } stbrp_node nodes[width]; stbrp_init_target(ctx, width, height, nodes, width); int packed = stbrp_pack_rects(ctx, rects, num); if (!packed) { return JS_NULL; } ret = JS_NewArray(js); for (int i = 0; i < num; i++) { HMM_Vec2 pos; pos.x = rects[i].x; pos.y = rects[i].y; JS_SetPropertyUint32(js, ret, i, vec22js(js,pos)); } ) // input: (gif image data) JSC_CCALL(os_make_gif, size_t rawlen; void *raw = js_get_blob_data(js, &rawlen, argv[0]); if (raw == (void*)-1) return JS_EXCEPTION; if (!raw) return JS_ThrowReferenceError(js, "could not load gif from supplied array buffer"); int n; int frames; int *delays; int width; int height; void *pixels = stbi_load_gif_from_memory(raw, rawlen, &delays, &width, &height, &frames, &n, 4); if (!pixels) { return JS_ThrowReferenceError(js, "1decode GIF: %s", stbi_failure_reason()); } // Always return an array of surfaces, even for single frame JSValue surface_array = JS_NewArray(js); ret = surface_array; for (int i = 0; i < frames; i++) { // Create surface data object JSValue surfData = JS_NewObject(js); JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, width)); JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, height)); JS_SetPropertyStr(js, surfData, "format", JS_NewString(js, "rgba32")); JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, width*4)); void *frame_pixels = (unsigned char*)pixels+(width*height*4*i); JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, frame_pixels, width*height*4)); // Add time property for animation frames if (frames > 1 && delays) { JS_SetPropertyStr(js, surfData, "time", number2js(js,(float)delays[i]/1000.0)); } JS_SetPropertyUint32(js, surface_array, i, surfData); } CLEANUP: if (delays) free(delays); if (pixels) free(pixels); ) JSValue aseframe2js(JSContext *js, ase_frame_t aframe) { JSValue frame = JS_NewObject(js); // Create surface data object instead of SDL_Surface JSValue surfData = JS_NewObject(js); JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, aframe.ase->w)); JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, aframe.ase->h)); JS_SetPropertyStr(js, surfData, "format", JS_NewString(js, "rgba32")); JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, aframe.ase->w*4)); JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, aframe.pixels, aframe.ase->w*aframe.ase->h*4)); JS_SetPropertyStr(js, frame, "surface", surfData); JS_SetPropertyStr(js, frame, "time", number2js(js,(float)aframe.duration_milliseconds/1000.0)); return frame; } // input: (aseprite data) JSC_CCALL(os_make_aseprite, size_t rawlen; void *raw = js_get_blob_data(js,&rawlen,argv[0]); if (raw == (void*)-1) return JS_EXCEPTION; if (!raw) return JS_ThrowReferenceError(js, "could not load aseprite from supplied array buffer: no data present"); ase_t *ase = cute_aseprite_load_from_memory(raw, rawlen, NULL); if (!ase) return JS_ThrowReferenceError(js, "could not load aseprite from supplied array buffer: %s", aseprite_GetError()); if (ase->tag_count == 0) { // we're dealing with a single frame image, or single animation if (ase->frame_count == 1) { JSValue obj = aseframe2js(js,ase->frames[0]); cute_aseprite_free(ase); return obj; } else { // Multiple frames but no tags - create a simple animation JSValue obj = JS_NewObject(js); JSValue frames = JS_NewArray(js); for (int f = 0; f < ase->frame_count; f++) { JSValue frame = aseframe2js(js,ase->frames[f]); JS_SetPropertyUint32(js, frames, f, frame); } JS_SetPropertyStr(js, obj, "frames", frames); JS_SetPropertyStr(js, obj, "loop", JS_NewBool(js, true)); ret = obj; cute_aseprite_free(ase); return ret; } } JSValue obj = JS_NewObject(js); for (int t = 0; t < ase->tag_count; t++) { ase_tag_t tag = ase->tags[t]; JSValue anim = JS_NewObject(js); JS_SetPropertyStr(js, anim, "repeat", number2js(js,tag.repeat)); switch(tag.loop_animation_direction) { case ASE_ANIMATION_DIRECTION_FORWARDS: JS_SetPropertyStr(js, anim, "loop", JS_NewString(js,"forward")); break; case ASE_ANIMATION_DIRECTION_BACKWORDS: JS_SetPropertyStr(js, anim, "loop", JS_NewString(js,"backward")); break; case ASE_ANIMATION_DIRECTION_PINGPONG: JS_SetPropertyStr(js, anim, "loop", JS_NewString(js,"pingpong")); break; } int _frame = 0; JSValue frames = JS_NewArray(js); for (int f = tag.from_frame; f <= tag.to_frame; f++) { JSValue frame = aseframe2js(js,ase->frames[f]); JS_SetPropertyUint32(js, frames, _frame, frame); _frame++; } JS_SetPropertyStr(js, anim, "frames", frames); JS_SetPropertyStr(js, obj, tag.name, anim); } ret = obj; cute_aseprite_free(ase); ) const JSCFunctionListEntry js_graphics_funcs[] = { MIST_FUNC_DEF(os, rectpack, 3), MIST_FUNC_DEF(os, image_decode, 1), MIST_FUNC_DEF(os, make_gif, 1), MIST_FUNC_DEF(os, make_aseprite, 1), MIST_FUNC_DEF(os, make_line_prim, 5), MIST_FUNC_DEF(graphics, hsl_to_rgb, 3), }; CELL_USE_FUNCS(js_graphics_funcs)