refactor out duplicate and dead code
This commit is contained in:
19
CLAUDE.md
19
CLAUDE.md
@@ -1,16 +1,7 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Build Commands
|
||||
|
||||
### Build variants
|
||||
- `make` - Make and install debug version. Usually all that's needed.
|
||||
- `make fast` - Build optimized version
|
||||
- `make release` - Build release version with LTO and optimizations
|
||||
- `make small` - Build minimal size version
|
||||
- `make web` - Build for web/emscripten platform
|
||||
- `make crosswin` - Cross-compile for Windows using mingw32
|
||||
Run 'make' to make cell.
|
||||
|
||||
### Testing
|
||||
After install with 'make', just run 'cell' and point it at the actor you want to launch. "cell tests/toml" runs the actor "tests/toml.js"
|
||||
@@ -18,15 +9,9 @@ After install with 'make', just run 'cell' and point it at the actor you want to
|
||||
## Scripting language
|
||||
This is called "cell", a variant of JavaScript with important differences. See docs/cell.md for detailed language documentation.
|
||||
|
||||
### Common development commands
|
||||
- `meson setup build_<variant>` - Configure build directory
|
||||
- `meson compile -C build_<variant>` - Compile in build directory
|
||||
- `./build_dbg/prosperon examples/<example>` - Run example from build directory
|
||||
- Copy prosperon to game directory and run: `cp build_dbg/prosperon <game-dir>/ && cd <game-dir> && ./prosperon`
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
Prosperon is an actor-based game engine inspired by Douglas Crockford's Misty system. Key architectural principles:
|
||||
Prosperon is an actor-based game engine.
|
||||
|
||||
### Actor Model
|
||||
- Each actor runs on its own thread
|
||||
|
||||
@@ -329,7 +329,7 @@ src += [
|
||||
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
||||
'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c',
|
||||
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_sdl_gpu.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
|
||||
'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c', 'qjs_layout.c'
|
||||
'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c', 'qjs_staef.c', 'qjs_layout.c', 'cell_qoi.c'
|
||||
]
|
||||
# quirc src
|
||||
src += [
|
||||
|
||||
@@ -8,6 +8,7 @@ var graphics = use('graphics')
|
||||
var util = use('util')
|
||||
var input = use('input')
|
||||
var prosperon = use('prosperon')
|
||||
var staef = use('staef')
|
||||
|
||||
function normalizeSpacing(spacing) {
|
||||
if (typeof spacing == 'number') {
|
||||
@@ -216,7 +217,7 @@ clay.text = function text(str, ...configs)
|
||||
var config = rectify_configs(configs);
|
||||
config.size ??= [0,0];
|
||||
config.font = graphics.get_font(config.font)
|
||||
var tsize = graphics.font_text_size(config.font, str, 0, config.size.x);
|
||||
var tsize = config.font.text_size(config.font, str, 0, config.size.x);
|
||||
tsize.x = Math.ceil(tsize.x)
|
||||
tsize.y = Math.ceil(tsize.y)
|
||||
config.size = config.size.map((x,i) => Math.max(x, tsize[i]));
|
||||
@@ -240,7 +241,7 @@ clay.button = function button(str, action, config = {})
|
||||
{
|
||||
config.__proto__ = button_base;
|
||||
config.font = graphics.get_font(config.font)
|
||||
config.size = graphics.font_text_size(config.font, str, 0, 0)
|
||||
config.size = confit.font.text_size(config.font, str, 0, 0)
|
||||
add_item(config);
|
||||
config.text = str;
|
||||
config.action = action;
|
||||
@@ -251,7 +252,7 @@ clay.textbox = function(str, on_change, ...configs) {
|
||||
config.on_change = on_change
|
||||
config.text = str
|
||||
config.font = graphics.get_font(config.font)
|
||||
var tsize = graphics.font_text_size(config.font, str, 0, 0)
|
||||
var tsize = config.font.text_size(config.font, str, 0, 0)
|
||||
config.size ??= [0,0]
|
||||
config.size = [Math.ceil(tsize.x), Math.ceil(tsize.y)]
|
||||
config.size = [Math.max(config.size[0], config.size[0]), Math.max(config.size[1], config.size[1])]
|
||||
|
||||
@@ -703,7 +703,7 @@ cmd_fns.draw_text = function(cmd)
|
||||
var font = graphics.get_font(cmd.font)
|
||||
if (!font[GPU])
|
||||
font[GPU] = get_img_gpu(font.surface)
|
||||
var mesh = graphics.make_text_buffer(
|
||||
var mesh = font.make_text_buffer(
|
||||
cmd.text,
|
||||
cmd.pos,
|
||||
[cmd.material.color.r, cmd.material.color.g, cmd.material.color.b, cmd.material.color.a],
|
||||
@@ -759,7 +759,6 @@ cmd_fns.draw_slice9 = function(cmd)
|
||||
var img = graphics.texture(cmd.image)
|
||||
if (!img) return
|
||||
|
||||
// Use the gpu_slice9 function from geometry module to generate the mesh
|
||||
var slice_info = {
|
||||
tile_top: true,
|
||||
tile_bottom: true,
|
||||
|
||||
@@ -102,8 +102,6 @@ tilemap.prototype =
|
||||
for (var y = 0; y < this.tiles[x].length; y++) {
|
||||
var tile = this.tiles[x][y];
|
||||
if (tile) {
|
||||
// tile should already be an image object
|
||||
// Create a unique key for each distinct image object
|
||||
if (!imageToKey.has(tile)) {
|
||||
var key = `texture_${keyCounter++}`;
|
||||
imageToKey.set(tile, key);
|
||||
|
||||
@@ -49,7 +49,7 @@ log.console = function(msg)
|
||||
|
||||
log.error = function(msg = new Error())
|
||||
{
|
||||
var caller = caller_data(4)
|
||||
var caller = caller_data(1)
|
||||
|
||||
if (msg instanceof Error)
|
||||
msg = msg + "\n" + msg.stack
|
||||
|
||||
@@ -5,6 +5,8 @@ var time = use('time')
|
||||
var res = use('resources')
|
||||
var json = use('json')
|
||||
var os = use('os')
|
||||
var staef = use('staef')
|
||||
var qoi = use('qoi')
|
||||
|
||||
var LASTUSE = Symbol()
|
||||
var LOADING = Symbol()
|
||||
@@ -95,7 +97,13 @@ function decode_image(bytes, ext)
|
||||
case 'gif': return graphics.make_gif(bytes) // returns array of surfaces
|
||||
case 'ase':
|
||||
case 'aseprite': return graphics.make_aseprite(bytes)
|
||||
default: return graphics.make_texture(bytes) // returns single surface
|
||||
case 'qoi': return qoi.decode(bytes) // returns single surface
|
||||
default:
|
||||
// Try QOI first since it's fast to check
|
||||
var qoi_result = qoi.decode(bytes)
|
||||
if (qoi_result) return qoi_result
|
||||
// Fall back to make_texture for other formats
|
||||
return graphics.image_decode(bytes) // returns single surface
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,8 +229,84 @@ graphics.texture = function texture(path) {
|
||||
|
||||
if (!cache[id]) {
|
||||
var ipath = res.find_image(id)
|
||||
if (!ipath)
|
||||
|
||||
// If not found normally, check in accio/32k folder
|
||||
if (!ipath) {
|
||||
// First check if we have a cached QOI version
|
||||
var cache_dir = '.prosperon/texture_cache'
|
||||
var qoi_cache_path = `${cache_dir}/${id}.qoi`
|
||||
|
||||
if (io.exists(qoi_cache_path)) {
|
||||
// Load the cached QOI file
|
||||
var qoi_bytes = io.slurpbytes(qoi_cache_path)
|
||||
var cached_img = qoi.decode(qoi_bytes)
|
||||
if (cached_img) {
|
||||
var result = new graphics.Image(cached_img)
|
||||
cache[id] = result
|
||||
return cache[id]
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find the file in accio/32k with any extension
|
||||
var accio_base = `accio/32k/${id}`
|
||||
var found_path = null
|
||||
|
||||
// Check for common image extensions
|
||||
var extensions = ['', '.qoi', '.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.gif', '.ase', '.aseprite']
|
||||
for (var ext of extensions) {
|
||||
var test_path = accio_base + ext
|
||||
if (io.exists(test_path)) {
|
||||
found_path = test_path
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (found_path) {
|
||||
// Load the 32k image
|
||||
var bytes = io.slurpbytes(found_path)
|
||||
var img_32k = decode_image(bytes, found_path.ext())
|
||||
|
||||
// Handle single surface images
|
||||
if (img_32k && img_32k.width && img_32k.pixels) {
|
||||
// Get the surface module for scaling
|
||||
var surface = use('surface')
|
||||
|
||||
var surf_4k = surface.scale(img_32k, {
|
||||
width: Math.floor(img_32k.width / 8),
|
||||
height: Math.floor(img_32k.height / 8),
|
||||
mode: 'linear'
|
||||
})
|
||||
log.console(img_32k.pixels.length)
|
||||
log.console(surf_4k.pixels.length)
|
||||
log.console(json.encode(surf_4k))
|
||||
|
||||
var qoi_data = surface.compress_qoi(surf_4k)
|
||||
|
||||
log.console(json.encode(qoi_data))
|
||||
|
||||
if (qoi_data && qoi_data.pixels) {
|
||||
if (!io.exists(cache_dir)) {
|
||||
io.mkdir(cache_dir)
|
||||
}
|
||||
io.slurpwrite(qoi_cache_path, qoi_data.pixels)
|
||||
}
|
||||
|
||||
// Use the 4k version
|
||||
var result = new graphics.Image(surf_4k)
|
||||
cache[id] = result
|
||||
return cache[id]
|
||||
}
|
||||
|
||||
// For now, if it's not a simple surface, just return it as-is
|
||||
// (animations, etc. would need more complex handling)
|
||||
var result = create_image(found_path)
|
||||
cache[id] = result
|
||||
return cache[id]
|
||||
}
|
||||
|
||||
// If still not found, return notex
|
||||
return graphics.texture('notex')
|
||||
}
|
||||
|
||||
var result = create_image(ipath)
|
||||
cache[id] = result
|
||||
@@ -380,7 +464,7 @@ graphics.get_font = function get_font(path) {
|
||||
if (fontcache[fontstr]) return fontcache[fontstr]
|
||||
|
||||
var data = io.slurpbytes(fullpath)
|
||||
var font = graphics.make_font(data, size)
|
||||
var font = new staef.font(data, size)
|
||||
|
||||
fontcache[fontstr] = font
|
||||
|
||||
|
||||
180
source/cell_qoi.c
Normal file
180
source/cell_qoi.c
Normal file
@@ -0,0 +1,180 @@
|
||||
#include "cell_qoi.h"
|
||||
#include "qoi.h"
|
||||
#include "qjs_blob.h"
|
||||
#include "qjs_sdl.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <string.h>
|
||||
|
||||
// Helper function to check for integer overflow in size calculations
|
||||
static int check_size_overflow(size_t a, size_t b, size_t c, size_t *result)
|
||||
{
|
||||
if (a > SIZE_MAX / b) return 1;
|
||||
size_t temp = a * b;
|
||||
if (temp > SIZE_MAX / c) return 1;
|
||||
*result = temp * c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// QOI compression/encoding
|
||||
JSValue js_qoi_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
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);
|
||||
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");
|
||||
|
||||
// Get pixel format
|
||||
JSValue format_val = JS_GetPropertyStr(js, argv[0], "format");
|
||||
SDL_PixelFormat format = js2SDL_PixelFormat(js, format_val);
|
||||
JS_FreeValue(js, format_val);
|
||||
|
||||
if (format == SDL_PIXELFORMAT_UNKNOWN)
|
||||
return JS_ThrowTypeError(js, "Invalid or missing pixel format");
|
||||
|
||||
// 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)
|
||||
return JS_ThrowTypeError(js, "pixels property must be a stoned blob");
|
||||
|
||||
// Validate buffer size
|
||||
int bytes_per_pixel = SDL_BYTESPERPIXEL(format);
|
||||
size_t required_size;
|
||||
if (check_size_overflow(width, height, bytes_per_pixel, &required_size)) {
|
||||
JS_FreeValue(js, pixels_val);
|
||||
return JS_ThrowRangeError(js, "Image dimensions too large");
|
||||
}
|
||||
|
||||
if (pixel_len < required_size)
|
||||
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]);
|
||||
}
|
||||
|
||||
// Determine number of channels based on format
|
||||
int channels = SDL_ISPIXELFORMAT_ALPHA(format) ? 4 : 3;
|
||||
|
||||
// Prepare QOI descriptor
|
||||
qoi_desc desc = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.channels = channels,
|
||||
.colorspace = colorspace
|
||||
};
|
||||
|
||||
// Encode to QOI
|
||||
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;
|
||||
}
|
||||
|
||||
// QOI decompression/decoding
|
||||
JSValue js_qoi_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) return JS_ThrowReferenceError(js, "could not get QOI data from array buffer");
|
||||
|
||||
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) {
|
||||
rgba_data = malloc(desc.width * desc.height * 4);
|
||||
if (!rgba_data) {
|
||||
free(data);
|
||||
return JS_ThrowOutOfMemory(js);
|
||||
}
|
||||
|
||||
// Convert RGB to RGBA
|
||||
unsigned char *src = (unsigned char*)data;
|
||||
unsigned char *dst = (unsigned char*)rgba_data;
|
||||
for (int i = 0; i < desc.width * desc.height; i++) {
|
||||
dst[i*4] = src[i*3];
|
||||
dst[i*4+1] = src[i*3+1];
|
||||
dst[i*4+2] = src[i*3+2];
|
||||
dst[i*4+3] = 255;
|
||||
}
|
||||
free(data);
|
||||
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));
|
||||
|
||||
free(rgba_data);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_qoi_funcs[] = {
|
||||
MIST_FUNC_DEF(qoi, encode, 1),
|
||||
MIST_FUNC_DEF(qoi, decode, 1)
|
||||
};
|
||||
|
||||
JSValue js_qoi_use(JSContext *js)
|
||||
{
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_qoi_funcs, countof(js_qoi_funcs));
|
||||
return mod;
|
||||
}
|
||||
8
source/cell_qoi.h
Normal file
8
source/cell_qoi.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef CELL_QOI_H
|
||||
#define CELL_QOI_H
|
||||
|
||||
#include <quickjs.h>
|
||||
|
||||
JSValue js_qoi_use(JSContext *js);
|
||||
|
||||
#endif // CELL_QOI_H
|
||||
@@ -26,9 +26,6 @@
|
||||
|
||||
#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_BOX
|
||||
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#include "stb_image_resize2.h"
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
|
||||
501
source/jsffi.c
501
source/jsffi.c
@@ -3,13 +3,10 @@
|
||||
#include "datastream.h"
|
||||
#include "qjs_sdl.h"
|
||||
#include "qjs_sdl_input.h"
|
||||
#include "qjs_io.h"
|
||||
#include "qjs_fd.h"
|
||||
#include "transform.h"
|
||||
#include "stb_ds.h"
|
||||
#include "stb_image.h"
|
||||
#include "stb_rect_pack.h"
|
||||
#include "stb_image_write.h"
|
||||
#include "string.h"
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
@@ -19,13 +16,11 @@
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include "render.h"
|
||||
#include "model.h"
|
||||
#include "HandmadeMath.h"
|
||||
#include "par/par_streamlines.h"
|
||||
#include "par/par_shapes.h"
|
||||
#include <stdint.h>
|
||||
#include "cute_aseprite.h"
|
||||
#include "cgltf.h"
|
||||
#include "cell.h"
|
||||
|
||||
#include "qjs_blob.h"
|
||||
@@ -57,52 +52,18 @@
|
||||
#include "qjs_utf8.h"
|
||||
#include "qjs_fit.h"
|
||||
#include "qjs_text.h"
|
||||
#include "qjs_staef.h"
|
||||
#include "qjs_io.h"
|
||||
#include "qjs_fd.h"
|
||||
#ifndef NSTEAM
|
||||
#include "qjs_steam.h"
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
void gui_input(SDL_Event *e);
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/utsname.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "wildmatch.h"
|
||||
|
||||
#include "freelist.h"
|
||||
|
||||
#include "sprite.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_gpu.h>
|
||||
#include <SDL3/SDL_error.h>
|
||||
#include <SDL3/SDL_properties.h>
|
||||
#include <SDL3/SDL_loadso.h>
|
||||
#include <SDL3/SDL_cpuinfo.h>
|
||||
|
||||
int randombytes(void *buf, size_t n);
|
||||
|
||||
int trace = 0;
|
||||
|
||||
// External transform function declarations
|
||||
extern JSClassID js_transform_id;
|
||||
JSValue transform2js(JSContext *js, transform *t);
|
||||
transform *js2transform(JSContext *js, JSValue v);
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Accelerate/Accelerate.h>
|
||||
//#else
|
||||
//#include <cblas.h>
|
||||
#endif
|
||||
|
||||
// Random number generation constants for MT19937-64
|
||||
#define NN STATE_VECTOR_LENGTH
|
||||
#define MM STATE_VECTOR_M
|
||||
@@ -233,39 +194,6 @@ JSValue js_getproperty(JSContext *js, JSValue v, const char *prop)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void free_gpu_buffer(JSRuntime *rt, void *opaque, void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index)
|
||||
{
|
||||
JSValue tstack[3];
|
||||
tstack[1] = JS_NULL;
|
||||
tstack[2] = JS_NULL;
|
||||
// TODO: always copying; implement "takeover"
|
||||
tstack[0] = js_new_blob_stoned_copy(js,data,size);//, make_gpu_buffer, NULL, 1);
|
||||
// JSValue ret = JS_NewTypedArray(js, 3, tstack, type);
|
||||
// JS_SetPropertyStr(js,ret,"stride", number2js(js,typed_array_bytes(type)*elements));
|
||||
// JS_SetPropertyStr(js,ret,"elen", number2js(js,typed_array_bytes(type)));
|
||||
// JS_SetPropertyStr(js,ret,"index", JS_NewBool(js,index));
|
||||
// JS_FreeValue(js,tstack[0]);
|
||||
// return ret;
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void *get_gpu_buffer(JSContext *js, JSValue argv, size_t *stride, size_t *size)
|
||||
{
|
||||
return NULL;
|
||||
/* size_t o, len, bytes, msize;
|
||||
JSValue buf = JS_GetTypedArrayBuffer(js, argv, &o, &len, &bytes);
|
||||
void *data = js_get_blob_data(js, &msize, buf);
|
||||
JS_FreeValue(js,buf);
|
||||
if (stride) *stride = js_getnum_str(js, argv, "stride");
|
||||
if (size) *size = msize;
|
||||
return data;*/
|
||||
}
|
||||
|
||||
JSValue make_quad_indices_buffer(JSContext *js, int quads)
|
||||
{
|
||||
cell_rt *rt = JS_GetContextOpaque(js);
|
||||
@@ -355,10 +283,6 @@ JSValue quads_to_mesh(JSContext *js, text_vert *buffer)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
typedef struct lrtb lrtb;
|
||||
|
||||
lrtb js2lrtb(JSContext *js, JSValue v)
|
||||
@@ -388,7 +312,6 @@ char *js2strdup(JSContext *js, JSValue v) {
|
||||
|
||||
#include "qjs_macros.h"
|
||||
|
||||
QJSCLASS(font,)
|
||||
QJSCLASS(datastream,)
|
||||
|
||||
JSValue angle2js(JSContext *js,double g) {
|
||||
@@ -613,27 +536,6 @@ irect js2irect(JSContext *js, JSValue v)
|
||||
return rect;
|
||||
}
|
||||
|
||||
rect transform_rect(SDL_Renderer *ren, rect in, HMM_Mat3 *t)
|
||||
{
|
||||
HMM_Vec3 bottom_left = (HMM_Vec3){in.x,in.y,1.0};
|
||||
HMM_Vec3 transformed_bl = HMM_MulM3V3(*t, bottom_left);
|
||||
in.x = transformed_bl.x;
|
||||
in.y = transformed_bl.y;
|
||||
in.y = in.y - in.h; // should be done for any platform that draws rectangles from top left
|
||||
return in;
|
||||
}
|
||||
|
||||
HMM_Vec2 transform_point(SDL_Renderer *ren, HMM_Vec2 in, HMM_Mat3 *t)
|
||||
{
|
||||
rect logical;
|
||||
SDL_GetRenderLogicalPresentationRect(ren, &logical);
|
||||
in.y *= -1;
|
||||
in.y += logical.h;
|
||||
in.x -= t->Columns[2].x;
|
||||
in.y -= t->Columns[2].y;
|
||||
return in;
|
||||
}
|
||||
|
||||
JSValue rect2js(JSContext *js,rect rect) {
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, obj, "x", number2js(js, rect.x));
|
||||
@@ -643,168 +545,6 @@ JSValue rect2js(JSContext *js,rect rect) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSValue ints2js(JSContext *js,int *ints) {
|
||||
JSValue arr = JS_NewArray(js);
|
||||
for (int i = 0; i < arrlen(ints); i++)
|
||||
JS_SetPropertyUint32(js, arr,i, number2js(js,ints[i]));
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
int vec_between(HMM_Vec2 p, HMM_Vec2 a, HMM_Vec2 b) {
|
||||
HMM_Vec2 n;
|
||||
n.x = b.x - a.x;
|
||||
n.y = b.y - a.y;
|
||||
n = HMM_NormV2(n);
|
||||
|
||||
return HMM_DotV2(n, HMM_SubV2(p,a)) > 0 && HMM_DotV2(HMM_MulV2F(n, -1), HMM_SubV2(p,b)) > 0;
|
||||
}
|
||||
|
||||
/* Determines between which two points in 'segs' point 'p' falls.
|
||||
0 indicates 'p' comes before the first point.
|
||||
arrlen(segs) indicates it comes after the last point.
|
||||
*/
|
||||
int point2segindex(HMM_Vec2 p, HMM_Vec2 *segs, double slop) {
|
||||
float shortest = slop < 0 ? INFINITY : slop;
|
||||
int best = -1;
|
||||
|
||||
for (int i = 0; i < arrlen(segs) - 1; i++) {
|
||||
float a = (segs[i + 1].y - segs[i].y) / (segs[i + 1].x - segs[i].x);
|
||||
float c = segs[i].y - (a * segs[i].x);
|
||||
float b = -1;
|
||||
|
||||
float dist = fabsf(a * p.x + b * p.y + c) / sqrt(pow(a, 2) + 1);
|
||||
|
||||
if (dist > shortest) continue;
|
||||
|
||||
int between = vec_between(p, segs[i], segs[i + 1]);
|
||||
|
||||
if (between) {
|
||||
shortest = dist;
|
||||
best = i + 1;
|
||||
} else {
|
||||
if (i == 0 && HMM_DistV2(p,segs[0]) < slop) {
|
||||
shortest = dist;
|
||||
best = i;
|
||||
} else if (i == arrlen(segs) - 2 && HMM_DistV2(p,arrlast(segs)) < slop) {
|
||||
shortest = dist;
|
||||
best = arrlen(segs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best == 1) {
|
||||
HMM_Vec2 n;
|
||||
n.x = segs[1].x - segs[0].x;
|
||||
n.y = segs[1].y - segs[0].y;
|
||||
n = HMM_NormV2(n);
|
||||
if (HMM_DotV2(n, HMM_SubV2(p,segs[0])) < 0 ){
|
||||
if (HMM_DistV2(p, segs[0]) >= slop)
|
||||
best = -1;
|
||||
else
|
||||
best = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (best == arrlen(segs) - 1) {
|
||||
HMM_Vec2 n;
|
||||
n.x = segs[best - 1].x - segs[best].x;
|
||||
n.y = segs[best - 1].y - segs[best - 1].y;
|
||||
n = HMM_NormV2(n);
|
||||
|
||||
if (HMM_DotV2(n, HMM_SubV2(p, segs[best])) < 0) {
|
||||
if (HMM_DistV2(p, segs[best]) >= slop)
|
||||
best = -1;
|
||||
else
|
||||
best = arrlen(segs);
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
JSC_CCALL(os_make_text_buffer,
|
||||
const char *s = JS_ToCString(js, argv[0]);
|
||||
rect rectpos = js2rect(js,argv[1]);
|
||||
font *f = js2font(js,argv[4]);
|
||||
colorf c = js2color(js,argv[2]);
|
||||
int wrap = js2number(js,argv[3]);
|
||||
HMM_Vec2 startpos = {.x = rectpos.x, .y = rectpos.y };
|
||||
text_vert *buffer = renderText(s, startpos, f, c, wrap);
|
||||
ret = quads_to_mesh(js,buffer);
|
||||
JS_FreeCString(js, s);
|
||||
arrfree(buffer);
|
||||
)
|
||||
|
||||
JSValue js_util_camera_globals(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
JSValue camera = argv[0];
|
||||
if(JS_IsNull(camera)) return JS_NULL;
|
||||
|
||||
HMM_Vec2 size; HMM_Vec3 pos; HMM_Quat rotation;
|
||||
double fov = 0; int ortho; double near_z = 0; double far_z = 0;
|
||||
HMM_Vec2 anchor;
|
||||
|
||||
JS_GETPROP(js, size, camera, size, vec2)
|
||||
JS_GETPROP(js, fov, camera, fov, number)
|
||||
JS_GETPROP(js, ortho, camera, ortho, bool)
|
||||
JS_GETPROP(js, near_z, camera, near_z, number)
|
||||
JS_GETPROP(js, far_z, camera, far_z, number)
|
||||
JS_GETPROP(js, anchor, camera, anchor, vec2)
|
||||
JS_GETPROP(js, pos, camera, pos, vec3)
|
||||
JS_GETPROP(js, rotation, camera, rotation, quat)
|
||||
|
||||
rotation.w = 1;
|
||||
|
||||
HMM_Mat4 proj, view;
|
||||
|
||||
if(ortho) {
|
||||
float left = -anchor.x * size.x;
|
||||
float bottom = -anchor.y * size.y;
|
||||
float right = left + size.x;
|
||||
float top = bottom + size.y;
|
||||
proj = HMM_Orthographic_RH_NO(left, right, bottom, top, -1.0f, 1.0f);
|
||||
} else {
|
||||
proj = HMM_Perspective_RH_NO(fov, size.x/size.y, near_z, far_z);
|
||||
proj.Columns[1] = HMM_MulV4F(proj.Columns[1], -1.0f);
|
||||
}
|
||||
|
||||
view = HMM_MulM4(
|
||||
HMM_InvTranslate(HMM_Translate(pos)),
|
||||
HMM_InvRotate (HMM_QToM4(rotation))
|
||||
);
|
||||
|
||||
JSValue data = JS_NewObject(js);
|
||||
|
||||
HMM_Mat4 world_to_projection = HMM_MulM4(proj, view);
|
||||
HMM_Mat4 projection_to_world = HMM_InvGeneralM4(world_to_projection);
|
||||
HMM_Vec3 camera_dir_world = HMM_NormV3(
|
||||
HMM_QVRot((HMM_Vec3){0,0,-1}, rotation)
|
||||
);
|
||||
|
||||
JS_SetPropertyStr(js, data, "world_to_projection",
|
||||
js_new_blob_stoned_copy(js, world_to_projection.em,
|
||||
sizeof(float)*16));
|
||||
JS_SetPropertyStr(js, data, "projection_to_world",
|
||||
js_new_blob_stoned_copy(js, projection_to_world.em,
|
||||
sizeof(float)*16));
|
||||
JS_SetPropertyStr(js, data, "world_to_view",
|
||||
js_new_blob_stoned_copy(js, view.em, sizeof(float)*16));
|
||||
JS_SetPropertyStr(js, data, "view_to_projection",
|
||||
js_new_blob_stoned_copy(js, proj.em, sizeof(float)*16));
|
||||
|
||||
JS_SetPropertyStr(js, data, "camera_pos_world", vec32js(js, pos));
|
||||
JS_SetPropertyStr(js, data, "camera_dir_world", vec32js(js, camera_dir_world));
|
||||
JS_SetPropertyStr(js, data, "render_size", vec22js(js, size));
|
||||
JS_SetPropertyStr(js, data, "viewport_size", vec22js(js, (HMM_Vec2){0.5,0.5}));
|
||||
JS_SetPropertyStr(js, data, "viewport_offset", vec22js(js, (HMM_Vec2){0,0}));
|
||||
JS_SetPropertyStr(js, data, "viewport_min_z", number2js(js, near_z));
|
||||
JS_SetPropertyStr(js, data, "viewport_max_z", number2js(js, far_z));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
static JSValue floats2array(JSContext *js, float *vals, size_t len) {
|
||||
JSValue arr = JS_NewArray(js);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
@@ -865,21 +605,6 @@ JSValue js_array_set_x(JSContext *js, JSValue self, JSValue val) { JS_SetPropert
|
||||
JSValue js_array_get_y(JSContext *js, JSValue self) { return JS_GetPropertyUint32(js,self,1); }
|
||||
JSValue js_array_set_y(JSContext *js, JSValue self, JSValue val) { JS_SetPropertyUint32(js,self,1,val); return JS_NULL; }
|
||||
|
||||
JSValue js_array_get_xy(JSContext *js, JSValue self)
|
||||
{
|
||||
JSValue arr = JS_NewArray(js);
|
||||
JS_SetPropertyUint32(js,arr,0,JS_GetPropertyUint32(js,self,0));
|
||||
JS_SetPropertyUint32(js,arr,1,JS_GetPropertyUint32(js,self,1));
|
||||
return arr;
|
||||
}
|
||||
|
||||
JSValue js_array_set_xy(JSContext *js, JSValue self, JSValue v)
|
||||
{
|
||||
JS_SetPropertyUint32(js,self,0,JS_GetPropertyUint32(js,v,0));
|
||||
JS_SetPropertyUint32(js,self,1,JS_GetPropertyUint32(js,v,1));
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
// Single-value accessors
|
||||
|
||||
JSValue js_array_get_r(JSContext *js, JSValue self)
|
||||
@@ -907,43 +632,6 @@ JSValue js_array_set_a(JSContext *js, JSValue self, JSValue val)
|
||||
{ JS_SetPropertyUint32(js, self, 3, val); return JS_NULL; }
|
||||
|
||||
// Multi-value accessors
|
||||
|
||||
JSValue js_array_get_rgb(JSContext *js, JSValue self)
|
||||
{
|
||||
JSValue arr = JS_NewArray(js);
|
||||
JS_SetPropertyUint32(js, arr, 0, JS_GetPropertyUint32(js, self, 0));
|
||||
JS_SetPropertyUint32(js, arr, 1, JS_GetPropertyUint32(js, self, 1));
|
||||
JS_SetPropertyUint32(js, arr, 2, JS_GetPropertyUint32(js, self, 2));
|
||||
return arr;
|
||||
}
|
||||
|
||||
JSValue js_array_set_rgb(JSContext *js, JSValue self, JSValue val)
|
||||
{
|
||||
JS_SetPropertyUint32(js, self, 0, JS_GetPropertyUint32(js, val, 0));
|
||||
JS_SetPropertyUint32(js, self, 1, JS_GetPropertyUint32(js, val, 1));
|
||||
JS_SetPropertyUint32(js, self, 2, JS_GetPropertyUint32(js, val, 2));
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue js_array_get_rgba(JSContext *js, JSValue self)
|
||||
{
|
||||
JSValue arr = JS_NewArray(js);
|
||||
JS_SetPropertyUint32(js, arr, 0, JS_GetPropertyUint32(js, self, 0));
|
||||
JS_SetPropertyUint32(js, arr, 1, JS_GetPropertyUint32(js, self, 1));
|
||||
JS_SetPropertyUint32(js, arr, 2, JS_GetPropertyUint32(js, self, 2));
|
||||
JS_SetPropertyUint32(js, arr, 3, JS_GetPropertyUint32(js, self, 3));
|
||||
return arr;
|
||||
}
|
||||
|
||||
JSValue js_array_set_rgba(JSContext *js, JSValue self, JSValue val)
|
||||
{
|
||||
JS_SetPropertyUint32(js, self, 0, JS_GetPropertyUint32(js, val, 0));
|
||||
JS_SetPropertyUint32(js, self, 1, JS_GetPropertyUint32(js, val, 1));
|
||||
JS_SetPropertyUint32(js, self, 2, JS_GetPropertyUint32(js, val, 2));
|
||||
JS_SetPropertyUint32(js, self, 3, JS_GetPropertyUint32(js, val, 3));
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_array_funcs[] = {
|
||||
PROTO_FUNC_DEF(array, add, 1),
|
||||
PROTO_FUNC_DEF(array, sub, 1),
|
||||
@@ -952,13 +640,10 @@ static const JSCFunctionListEntry js_array_funcs[] = {
|
||||
PROTO_FUNC_DEF(array, lerp, 2),
|
||||
JS_CGETSET_DEF("x", js_array_get_x,js_array_set_x),
|
||||
JS_CGETSET_DEF("y", js_array_get_y, js_array_set_y),
|
||||
JS_CGETSET_DEF("xy", js_array_get_xy, js_array_set_xy),
|
||||
JS_CGETSET_DEF("r", js_array_get_r, js_array_set_r),
|
||||
JS_CGETSET_DEF("g", js_array_get_g, js_array_set_g),
|
||||
JS_CGETSET_DEF("b", js_array_get_b, js_array_set_b),
|
||||
JS_CGETSET_DEF("a", js_array_get_a, js_array_set_a),
|
||||
JS_CGETSET_DEF("rgb", js_array_get_rgb, js_array_set_rgb),
|
||||
JS_CGETSET_DEF("rgba", js_array_get_rgba, js_array_set_rgba),
|
||||
};
|
||||
|
||||
JSC_CCALL(number_lerp,
|
||||
@@ -1022,52 +707,8 @@ static const JSCFunctionListEntry js_datastream_funcs[] = {
|
||||
CGETSET_ADD(datastream, callback),
|
||||
};
|
||||
|
||||
JSC_GETSET(font, linegap, number)
|
||||
JSC_GET(font, height, number)
|
||||
JSC_GET(font, ascent, number)
|
||||
JSC_GET(font, descent, number)
|
||||
|
||||
JSC_CCALL(graphics_font_text_size,
|
||||
font *f = js2font(js,argv[0]);
|
||||
const char *str = JS_ToCString(js, argv[1]);
|
||||
float letterSpacing = js2number(js,argv[2]);
|
||||
float wrap = js2number(js,argv[3]);
|
||||
ret = vec22js(js,measure_text(str, f, letterSpacing, wrap));
|
||||
JS_FreeCString(js, str);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_font_funcs[] = {
|
||||
CGETSET_ADD(font, linegap),
|
||||
MIST_GET(font, height),
|
||||
MIST_GET(font, ascent),
|
||||
MIST_GET(font, descent),
|
||||
};
|
||||
|
||||
JSC_CCALL(os_image_info,
|
||||
size_t len;
|
||||
void *raw = js_get_blob_data(js, &len, argv[0]);
|
||||
if (!raw)
|
||||
return JS_ThrowReferenceError(js, "could not load image with array buffer");
|
||||
|
||||
int depth = stbi_is_16_bit_from_memory(raw, len) ? 16 : 8;
|
||||
int width, height, comp;
|
||||
if (!stbi_info_from_memory(raw, len, &width, &height, &comp))
|
||||
return JS_ThrowReferenceError(js, "could not parse image info: %s", stbi_failure_reason());
|
||||
|
||||
int hdr = stbi_is_hdr_from_memory(raw, len);
|
||||
|
||||
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, "components", JS_NewInt32(js, comp));
|
||||
JS_SetPropertyStr(js, obj, "depth", JS_NewInt32(js, depth));
|
||||
JS_SetPropertyStr(js, obj, "hdr", JS_NewBool(js, hdr));
|
||||
|
||||
ret = obj;
|
||||
)
|
||||
|
||||
// input: (encoded image data of jpg, png, bmp, tiff)
|
||||
JSC_CCALL(os_make_texture,
|
||||
JSC_CCALL(os_image_decode,
|
||||
size_t len;
|
||||
void *raw = js_get_blob_data(js, &len, argv[0]);
|
||||
if (!raw) return JS_ThrowReferenceError(js, "could not load texture with array buffer");
|
||||
@@ -1228,39 +869,6 @@ JSC_CCALL(os_make_aseprite,
|
||||
cute_aseprite_free(ase);
|
||||
)
|
||||
|
||||
JSC_CCALL(os_make_font,
|
||||
size_t len;
|
||||
void *data = js_get_blob_data(js,&len,argv[0]);
|
||||
if (!data) return JS_ThrowReferenceError(js, "could not get array buffer data");
|
||||
font *f = MakeFont(data, len, js2number(js,argv[1]));
|
||||
if (!f) return JS_ThrowReferenceError(js, "could not create font");
|
||||
ret = font2js(js,f);
|
||||
|
||||
// Create surface data object for the font's atlas
|
||||
if (f->surface) {
|
||||
JSValue surfData = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, f->surface->w));
|
||||
JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, f->surface->h));
|
||||
JS_SetPropertyStr(js, surfData, "format", pixelformat2js(js, f->surface->format));
|
||||
JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, f->surface->pitch));
|
||||
|
||||
// Lock surface if needed
|
||||
int locked = 0;
|
||||
if (SDL_MUSTLOCK(f->surface)) {
|
||||
if (SDL_LockSurface(f->surface) < 0)
|
||||
return JS_ThrowInternalError(js, "Lock surface failed: %s", SDL_GetError());
|
||||
locked = 1;
|
||||
}
|
||||
|
||||
size_t byte_size = f->surface->pitch * f->surface->h;
|
||||
JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, f->surface->pixels, byte_size));
|
||||
|
||||
if (locked)
|
||||
SDL_UnlockSurface(f->surface);
|
||||
|
||||
JS_SetPropertyStr(js, ret, "surface", surfData);
|
||||
}
|
||||
)
|
||||
|
||||
JSC_SCALL(os_system, ret = number2js(js,system(str)); )
|
||||
|
||||
@@ -1279,15 +887,6 @@ JSC_CCALL(os_srand,
|
||||
m_seedRand(mrand, js2number(js,argv[0]));
|
||||
)
|
||||
|
||||
JSValue make_color_buffer(JSContext *js, colorf c, int verts)
|
||||
{
|
||||
HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts);
|
||||
for (int i = 0; i < verts; i++)
|
||||
colordata[i] = c;
|
||||
|
||||
return make_gpu_buffer(js, colordata, sizeof(*colordata)*verts, 0, 4, 0, 0);
|
||||
}
|
||||
|
||||
JSC_CCALL(os_make_line_prim,
|
||||
return JS_NULL;
|
||||
/*
|
||||
@@ -1322,7 +921,6 @@ JSC_CCALL(os_make_line_prim,
|
||||
|
||||
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,"color",make_color_buffer(js,js2color(js,argv[4]), m->num_vertices));
|
||||
JS_SetPropertyStr(js,prim,"num_indices", number2js(js,m->num_triangles*3));
|
||||
JS_SetPropertyStr(js,prim,"first_index", number2js(js,0));
|
||||
|
||||
@@ -1397,60 +995,8 @@ JSC_CCALL(os_rectpack,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
JSC_CCALL(os_insertion_sort,
|
||||
JSValue arr = argv[0];
|
||||
JSValue cmp = argv[1];
|
||||
int len = JS_ArrayLength(js, arr);
|
||||
|
||||
for (int i = 1; i < len; i++) {
|
||||
JSValue key = JS_GetPropertyUint32(js, arr, i);
|
||||
int j = i - 1;
|
||||
|
||||
while (j >= 0) {
|
||||
JSValue arr_j = JS_GetPropertyUint32(js, arr, j);
|
||||
|
||||
JSValue ret = JS_Call(js, cmp, JS_NULL, 2, (JSValue[]){ arr_j, key });
|
||||
if (JS_IsException(ret)) {
|
||||
JS_FreeValue(js,arr_j);
|
||||
JS_FreeValue(js,key);
|
||||
return ret;
|
||||
}
|
||||
double c = js2number(js, ret);
|
||||
JS_FreeValue(js, ret);
|
||||
|
||||
if (c > 0) {
|
||||
JS_SetPropertyUint32(js, arr, j + 1, arr_j);
|
||||
j--;
|
||||
} else {
|
||||
JS_FreeValue(js, arr_j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JS_SetPropertyUint32(js, arr, j + 1, key);
|
||||
}
|
||||
|
||||
ret = JS_DupValue(js,arr);
|
||||
)
|
||||
|
||||
JSC_CCALL(os_power_state,
|
||||
SDL_PowerState state = SDL_GetPowerInfo(NULL, NULL);
|
||||
switch(state) {
|
||||
case SDL_POWERSTATE_ERROR: return JS_ThrowTypeError(js, "Error determining power status");
|
||||
case SDL_POWERSTATE_UNKNOWN: return JS_NULL;
|
||||
case SDL_POWERSTATE_ON_BATTERY: return JS_NewString(js, "on battery");
|
||||
case SDL_POWERSTATE_NO_BATTERY: return JS_NewString(js, "no battery");
|
||||
case SDL_POWERSTATE_CHARGING: return JS_NewString(js, "charging");
|
||||
case SDL_POWERSTATE_CHARGED: return JS_NewString(js, "charged");
|
||||
}
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_util_funcs[] = {
|
||||
MIST_FUNC_DEF(os, guid, 0),
|
||||
MIST_FUNC_DEF(os, insertion_sort, 2),
|
||||
MIST_FUNC_DEF(util, camera_globals, 1),
|
||||
};
|
||||
|
||||
JSC_CCALL(graphics_hsl_to_rgb,
|
||||
@@ -1473,47 +1019,13 @@ JSC_CCALL(graphics_hsl_to_rgb,
|
||||
return color2js(js, (colorf){r+m, g+m, b+m, 1});
|
||||
)
|
||||
|
||||
JSC_CCALL(graphics_save_png,
|
||||
const char *file = JS_ToCString(js, argv[0]);
|
||||
int w, h, comp, pitch;
|
||||
JS_ToInt32(js, &w, argv[1]);
|
||||
JS_ToInt32(js, &h, argv[2]);
|
||||
JS_ToInt32(js, &pitch, argv[4]);
|
||||
size_t size;
|
||||
void *data = js_get_blob_data(js, &size, argv[3]);
|
||||
|
||||
if (!stbi_write_png(file, w, h, 4, data, pitch))
|
||||
return JS_ThrowInternalError(js, "Could not write png");
|
||||
)
|
||||
|
||||
JSC_CCALL(graphics_save_jpg,
|
||||
const char *file = JS_ToCString(js, argv[0]);
|
||||
int w, h, comp, pitch, quality;
|
||||
JS_ToInt32(js, &w, argv[1]);
|
||||
JS_ToInt32(js, &h, argv[2]);
|
||||
JS_ToInt32(js, &pitch, argv[4]);
|
||||
JS_ToInt32(js, &quality, argv[5]);
|
||||
if (!quality) quality = 80;
|
||||
size_t size;
|
||||
void *data = js_get_blob_data(js, &size, argv[3]);
|
||||
|
||||
if (!stbi_write_jpg(file, w, h, 4, data, quality))
|
||||
return JS_ThrowInternalError(js, "Could not write png");
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_graphics_funcs[] = {
|
||||
MIST_FUNC_DEF(os, make_text_buffer, 5),
|
||||
MIST_FUNC_DEF(os, rectpack, 3),
|
||||
MIST_FUNC_DEF(os, image_info, 1),
|
||||
MIST_FUNC_DEF(os, make_texture, 1),
|
||||
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_font, 2),
|
||||
MIST_FUNC_DEF(os, make_line_prim, 5),
|
||||
MIST_FUNC_DEF(graphics, hsl_to_rgb, 3),
|
||||
MIST_FUNC_DEF(graphics, save_png, 4),
|
||||
MIST_FUNC_DEF(graphics, save_jpg, 4),
|
||||
MIST_FUNC_DEF(graphics, font_text_size, 4),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_video_funcs[] = {
|
||||
@@ -1592,6 +1104,7 @@ JSC_CCALL(os_value_id,
|
||||
#include "qjs_nota.h"
|
||||
#include "qjs_layout.h"
|
||||
#include "qjs_sdl_gpu.h"
|
||||
#include "cell_qoi.h"
|
||||
|
||||
JSValue js_imgui_use(JSContext *js);
|
||||
#define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use}
|
||||
@@ -1627,9 +1140,11 @@ void ffi_load(JSContext *js)
|
||||
arrput(rt->module_registry, MISTLINE(utf8));
|
||||
arrput(rt->module_registry, MISTLINE(fit));
|
||||
arrput(rt->module_registry, MISTLINE(text));
|
||||
arrput(rt->module_registry, MISTLINE(staef));
|
||||
arrput(rt->module_registry, MISTLINE(wota));
|
||||
arrput(rt->module_registry, MISTLINE(nota));
|
||||
arrput(rt->module_registry, MISTLINE(sdl_gpu));
|
||||
arrput(rt->module_registry, MISTLINE(qoi));
|
||||
|
||||
// power user
|
||||
arrput(rt->module_registry, MISTLINE(js));
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
#include "cell.h"
|
||||
|
||||
JSValue js_crypto_use(JSContext *ctx);
|
||||
int randombytes(void *buf, size_t n);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -473,117 +473,6 @@ static HMM_Vec3 base_quad[4] = {
|
||||
{1.0,1.0,1.0}
|
||||
};
|
||||
|
||||
JSC_CCALL(gpu_make_sprite_mesh,
|
||||
size_t quads = JS_ArrayLength(js, argv[0]);
|
||||
size_t verts = quads*4;
|
||||
size_t count = quads*6;
|
||||
|
||||
// Prepare arrays on CPU
|
||||
HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts);
|
||||
HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts);
|
||||
HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts);
|
||||
|
||||
for (int i = 0; i < quads; i++) {
|
||||
JSValue sub = JS_GetPropertyUint32(js,argv[0],i);
|
||||
|
||||
transform *tr;
|
||||
rect src;
|
||||
HMM_Vec4 color;
|
||||
JS_GETATOM(js,src,sub,src,rect)
|
||||
JS_GETATOM(js,color,sub,color,color)
|
||||
JS_GETATOM(js,tr,sub,transform,transform)
|
||||
JS_FreeValue(js,sub);
|
||||
|
||||
size_t base = i*4;
|
||||
if (tr) {
|
||||
HMM_Mat3 trmat = transform2mat3(tr);
|
||||
for (int j = 0; j < 4; j++)
|
||||
posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy;
|
||||
} else {
|
||||
rect dst;
|
||||
JS_GETATOM(js,dst,sub,rect,rect);
|
||||
posdata[base+0] = (HMM_Vec2){dst.x,dst.y};
|
||||
posdata[base + 1] = (HMM_Vec2){ dst.x+dst.w, dst.y };
|
||||
posdata[base + 2] = (HMM_Vec2){ dst.x, dst.y+dst.h };
|
||||
posdata[base + 3] = (HMM_Vec2){ dst.x+dst.w, dst.y+dst.h };
|
||||
}
|
||||
|
||||
uvdata[base+0] = (HMM_Vec2){src.x, src.y+src.h};
|
||||
uvdata[base+1] = (HMM_Vec2){src.x+src.w, src.y+src.h};
|
||||
uvdata[base+2] = (HMM_Vec2){src.x, src.y};
|
||||
uvdata[base+3] = (HMM_Vec2){src.x+src.w, src.y};
|
||||
|
||||
colordata[base+0] = color;
|
||||
colordata[base+1] = color;
|
||||
colordata[base+2] = color;
|
||||
colordata[base+3] = color;
|
||||
}
|
||||
|
||||
// Check old mesh
|
||||
JSValue old_mesh = JS_NULL;
|
||||
if (argc > 1)
|
||||
old_mesh = argv[1];
|
||||
|
||||
// Needed sizes
|
||||
size_t pos_size = sizeof(*posdata)*verts;
|
||||
size_t uv_size = sizeof(*uvdata)*verts;
|
||||
size_t color_size = sizeof(*colordata)*verts;
|
||||
|
||||
BufferCheckResult pos_chk = get_or_extend_buffer(js, old_mesh, "pos", pos_size, 0, 2, 1, 0);
|
||||
BufferCheckResult uv_chk = get_or_extend_buffer(js, old_mesh, "uv", uv_size, 0, 2, 1, 0);
|
||||
BufferCheckResult color_chk = get_or_extend_buffer(js, old_mesh, "color", color_size, 0, 4, 1, 0);
|
||||
|
||||
int need_new_all = pos_chk.need_new || uv_chk.need_new || color_chk.need_new;
|
||||
|
||||
ret = JS_NewObject(js);
|
||||
|
||||
if (need_new_all) {
|
||||
// Create all new buffers
|
||||
JSValue new_pos = make_gpu_buffer(js, posdata, pos_size, 0, 2, 1,0);
|
||||
JSValue new_uv = make_gpu_buffer(js, uvdata, uv_size, 0, 2, 1,0);
|
||||
JSValue new_color = make_gpu_buffer(js, colordata, color_size, 0, 0, 1,0);
|
||||
|
||||
JS_SetPropertyStr(js, ret, "pos", new_pos);
|
||||
JS_SetPropertyStr(js, ret, "uv", new_uv);
|
||||
JS_SetPropertyStr(js, ret, "color", new_color);
|
||||
|
||||
// Indices
|
||||
JSValue indices = make_quad_indices_buffer(js, quads);
|
||||
JS_SetPropertyStr(js, ret, "indices", indices);
|
||||
} else {
|
||||
// Reuse the old buffers
|
||||
// Just copy data into existing buffers via their pointers
|
||||
memcpy(pos_chk.ptr, posdata, pos_size);
|
||||
memcpy(uv_chk.ptr, uvdata, uv_size);
|
||||
memcpy(color_chk.ptr, colordata, color_size);
|
||||
|
||||
// Duplicate old references since we're returning a new object
|
||||
JS_SetPropertyStr(js, ret, "pos", JS_DupValue(js, pos_chk.val));
|
||||
JS_SetPropertyStr(js, ret, "uv", JS_DupValue(js, uv_chk.val));
|
||||
JS_SetPropertyStr(js, ret, "color", JS_DupValue(js, color_chk.val));
|
||||
|
||||
// Indices can remain the same if they were also large enough. If using a shared global index buffer:
|
||||
JSValue indices = make_quad_indices_buffer(js, quads);
|
||||
JS_SetPropertyStr(js, ret, "indices", indices);
|
||||
}
|
||||
|
||||
JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts));
|
||||
JS_SetPropertyStr(js, ret, "count", number2js(js, count));
|
||||
JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count));
|
||||
|
||||
// Free temporary CPU arrays
|
||||
free(posdata);
|
||||
free(uvdata);
|
||||
free(colordata);
|
||||
|
||||
// Free old buffer values if they were fetched
|
||||
if (!JS_IsNull(pos_chk.val)) JS_FreeValue(js, pos_chk.val);
|
||||
if (!JS_IsNull(uv_chk.val)) JS_FreeValue(js, uv_chk.val);
|
||||
if (!JS_IsNull(color_chk.val)) JS_FreeValue(js, color_chk.val);
|
||||
|
||||
return ret;
|
||||
)
|
||||
|
||||
struct quad_buffers {
|
||||
HMM_Vec2 *pos;
|
||||
HMM_Vec2 *uv;
|
||||
@@ -601,27 +490,6 @@ struct quad_buffers quad_buffers_new(int verts)
|
||||
return b;
|
||||
}
|
||||
|
||||
JSValue quadbuffers_to_mesh(JSContext *js, struct quad_buffers buffers)
|
||||
{
|
||||
JSValue jspos = make_gpu_buffer(js, buffers.pos, sizeof(HMM_Vec2)*buffers.verts, 0, 2, 0, 0);
|
||||
JSValue jsuv = make_gpu_buffer(js, buffers.uv, sizeof(HMM_Vec2)*buffers.verts, 0, 2,0,0);
|
||||
JSValue jscolor = make_gpu_buffer(js, buffers.color, sizeof(HMM_Vec4)*buffers.verts, 0, 4,0,0);
|
||||
|
||||
size_t quads = buffers.verts/4;
|
||||
size_t count = buffers.verts/2*3;
|
||||
JSValue jsidx = make_quad_indices_buffer(js, quads);
|
||||
|
||||
JSValue ret = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, ret, "pos", jspos);
|
||||
JS_SetPropertyStr(js, ret, "uv", jsuv);
|
||||
JS_SetPropertyStr(js, ret, "color", jscolor);
|
||||
JS_SetPropertyStr(js, ret, "indices", jsidx);
|
||||
JS_SetPropertyStr(js, ret, "vertices", number2js(js, buffers.verts));
|
||||
JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sort_sprite(const sprite *a, const sprite *b)
|
||||
{
|
||||
if (a->layer != b->layer) return a->layer - b->layer;
|
||||
@@ -634,142 +502,6 @@ int sort_sprite(const sprite *a, const sprite *b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSC_CCALL(gpu_make_sprite_queue,
|
||||
sprite *sprites = NULL;
|
||||
size_t quads = 0;
|
||||
int needfree = 1;
|
||||
|
||||
if (js_is_blob(js, argv[0])) {
|
||||
// test for fastest
|
||||
size_t size;
|
||||
sprite *sprites = js_get_blob_data(js, &size, argv[0]);
|
||||
quads = size/sizeof(*sprites);
|
||||
needfree = 0;
|
||||
for (int i = 0; i < quads; i++)
|
||||
JS_DupValue(js,sprites[i].image);
|
||||
} else {
|
||||
quads = JS_ArrayLength(js, argv[0]);
|
||||
if (quads == 0)
|
||||
return JS_ThrowReferenceError(js, "Expected an array of sprites with length > 0.");
|
||||
|
||||
arrsetcap(sprites, quads);
|
||||
|
||||
for (int i = 0; i < quads; i++) {
|
||||
JSValue sub = JS_GetPropertyUint32(js, argv[0], i);
|
||||
sprite *jsp = js2sprite(js, sub);
|
||||
if (jsp) {
|
||||
arrput(sprites, *jsp);
|
||||
JS_DupValue(js,jsp->image);
|
||||
}
|
||||
else {
|
||||
sprite sp = {0};
|
||||
JS_GETATOM(js, sp.pos, sub, pos, vec2)
|
||||
JS_GETATOM(js, sp.center, sub, center, vec2)
|
||||
JS_GETATOM(js, sp.skew, sub, skew, vec2)
|
||||
JS_GETATOM(js, sp.scale, sub, scale, vec2)
|
||||
JS_GETATOM(js, sp.color,sub,color,color)
|
||||
JS_GETATOM(js, sp.layer,sub,layer,number)
|
||||
sp.image = JS_GetPropertyStr(js,sub,"image");
|
||||
sprite_apply(&sp);
|
||||
arrput(sprites,sp);
|
||||
}
|
||||
JS_FreeValue(js, sub);
|
||||
}
|
||||
}
|
||||
|
||||
qsort(sprites, quads, sizeof(sprite), sort_sprite);
|
||||
|
||||
struct quad_buffers buffers = quad_buffers_new(quads*4);
|
||||
|
||||
const HMM_Vec2 local[4] = { {0,0}, {1,0}, {0,1}, {1,1} };
|
||||
|
||||
rect uv;
|
||||
rect uv_px;
|
||||
JSValue cur_img = JS_NULL;
|
||||
|
||||
for (size_t i = 0; i < quads; i++) {
|
||||
sprite *s = &sprites[i];
|
||||
if (JS_IsNull(cur_img) || !JS_StrictEq(js, s->image, cur_img)) {
|
||||
cur_img = s->image;
|
||||
JS_GETATOM(js, uv, cur_img, rect, rect)
|
||||
JS_GETATOM(js, uv_px, cur_img, rect_px, rect)
|
||||
}
|
||||
|
||||
HMM_Vec2 px_size = {
|
||||
uv_px.w * s->scale.X,
|
||||
uv_px.h * s->scale.Y
|
||||
};
|
||||
|
||||
HMM_Vec2 anchor = {
|
||||
px_size.X * s->center.X,
|
||||
px_size.Y * s->center.Y
|
||||
};
|
||||
|
||||
size_t base = i * 4;
|
||||
|
||||
for (int v = 0; v < 4; v++) {
|
||||
HMM_Vec2 lp = {
|
||||
local[v].X * px_size.X - anchor.X,
|
||||
local[v].Y * px_size.Y - anchor.Y
|
||||
};
|
||||
|
||||
HMM_Vec2 world = HMM_AddV2(s->pos, HMM_MulM2V2(s->affine, lp));
|
||||
|
||||
buffers.pos[base + v] = world;
|
||||
buffers.color[base + v] = s->color;
|
||||
}
|
||||
|
||||
/* UVs are still top-left-origin pixel coords, so keep previous packing */
|
||||
buffers.uv[base + 0] = (HMM_Vec2){ uv.x, uv.y + uv.h };
|
||||
buffers.uv[base + 1] = (HMM_Vec2){ uv.x + uv.w, uv.y + uv.h };
|
||||
buffers.uv[base + 2] = (HMM_Vec2){ uv.x, uv.y };
|
||||
buffers.uv[base + 3] = (HMM_Vec2){ uv.x + uv.w, uv.y };
|
||||
}
|
||||
|
||||
JSValue mesh = quadbuffers_to_mesh(js, buffers);
|
||||
|
||||
ret = JS_NewArray(js);
|
||||
int first_index = 0;
|
||||
int count = 0;
|
||||
int n = 0;
|
||||
JSValue img = JS_NULL;
|
||||
|
||||
for (int i = 0; i < quads; i++) {
|
||||
if (!JS_SameValue(js, sprites[i].image, img)) {
|
||||
if (count > 0) {
|
||||
JSValue q = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry"));
|
||||
JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh));
|
||||
JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2]));
|
||||
JS_SetPropertyStr(js, q, "image", img);
|
||||
JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index));
|
||||
JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6));
|
||||
JS_SetPropertyUint32(js, ret, n++, q);
|
||||
}
|
||||
first_index = i*6;
|
||||
count = 1;
|
||||
img = JS_DupValue(js, sprites[i].image);
|
||||
} else count++;
|
||||
JS_FreeValue(js,sprites[i].image);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
JSValue q = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry"));
|
||||
JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh));
|
||||
JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2]));
|
||||
JS_SetPropertyStr(js, q, "image", img);
|
||||
JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index));
|
||||
JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6));
|
||||
JS_SetPropertyUint32(js, ret, n++, q);
|
||||
}
|
||||
|
||||
if (needfree)
|
||||
arrfree(sprites);
|
||||
|
||||
JS_FreeValue(js, mesh);
|
||||
)
|
||||
|
||||
JSC_CCALL(geometry_rect_transform,
|
||||
// argv[0] = world‐space rect
|
||||
rect r = js2rect(js, argv[0]);
|
||||
@@ -899,14 +631,11 @@ JSC_CCALL(geometry_tilemap_to_data,
|
||||
xy_data[base + 7] = world_y + size_y;
|
||||
|
||||
// Set UVs (normalized 0-1 for now)
|
||||
uv_data[base + 0] = 0.0f;
|
||||
uv_data[base + 1] = 0.0f;
|
||||
uv_data[base + 2] = 1.0f;
|
||||
uv_data[base + 3] = 0.0f;
|
||||
uv_data[base + 4] = 0.0f;
|
||||
uv_data[base + 5] = 1.0f;
|
||||
uv_data[base + 6] = 1.0f;
|
||||
uv_data[base + 7] = 1.0f;
|
||||
uv_data[base + 0] = 0; uv_data[base + 1] = 1.0f; // now samples the TOP of the image
|
||||
uv_data[base + 2] = 1.0f; uv_data[base + 3] = 1.0f;
|
||||
uv_data[base + 4] = 0; uv_data[base + 5] = 0;
|
||||
uv_data[base + 6] = 1.0f; uv_data[base + 7] = 0;
|
||||
|
||||
|
||||
// Set colors (check if tile has color property)
|
||||
SDL_FColor default_color = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
@@ -1270,82 +999,6 @@ JSC_CCALL(geometry_transform_xy_blob,
|
||||
ret = transformed_blob;
|
||||
)
|
||||
|
||||
// RENDERITEM SYSTEM
|
||||
typedef struct {
|
||||
int layer;
|
||||
float y;
|
||||
JSValue val;
|
||||
} RenderItem;
|
||||
|
||||
#define MAX_RENDER_ITEMS 64000
|
||||
static RenderItem render_items[MAX_RENDER_ITEMS];
|
||||
static int render_item_count = 0;
|
||||
|
||||
JSC_CCALL(geometry_renderitem_push,
|
||||
if (argc < 3) {
|
||||
return JS_ThrowTypeError(js, "renderitem_push requires 3 arguments: layer, y, val");
|
||||
}
|
||||
|
||||
if (render_item_count >= MAX_RENDER_ITEMS) {
|
||||
return JS_ThrowTypeError(js, "Maximum render items exceeded");
|
||||
}
|
||||
|
||||
int layer;
|
||||
double y;
|
||||
|
||||
if (JS_ToInt32(js, &layer, argv[0]) < 0) {
|
||||
return JS_ThrowTypeError(js, "layer must be a number");
|
||||
}
|
||||
|
||||
if (JS_ToFloat64(js, &y, argv[1]) < 0) {
|
||||
return JS_ThrowTypeError(js, "y must be a number");
|
||||
}
|
||||
|
||||
render_items[render_item_count].layer = layer;
|
||||
render_items[render_item_count].y = (float)y;
|
||||
render_items[render_item_count].val = JS_DupValue(js, argv[2]);
|
||||
render_item_count++;
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
static int compare_render_items(const void *a, const void *b)
|
||||
{
|
||||
const RenderItem *item_a = (const RenderItem *)a;
|
||||
const RenderItem *item_b = (const RenderItem *)b;
|
||||
|
||||
if (item_a->layer != item_b->layer) {
|
||||
return item_a->layer - item_b->layer;
|
||||
}
|
||||
|
||||
// if (item_a->y != item_b->y) {
|
||||
// return (item_b->y > item_a->y) ? 1 : -1;
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSC_CCALL(geometry_renderitem_sort,
|
||||
qsort(render_items, render_item_count, sizeof(RenderItem), compare_render_items);
|
||||
|
||||
JSValue result = JS_NewArray(js);
|
||||
|
||||
for (int i = 0; i < render_item_count; i++) {
|
||||
JS_SetPropertyUint32(js, result, i, JS_DupValue(js, render_items[i].val));
|
||||
}
|
||||
|
||||
return result;
|
||||
)
|
||||
|
||||
JSC_CCALL(geometry_renderitem_clear,
|
||||
for (int i = 0; i < render_item_count; i++) {
|
||||
JS_FreeValue(js, render_items[i].val);
|
||||
}
|
||||
render_item_count = 0;
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(geometry_array_blob,
|
||||
JSValue arr = argv[0];
|
||||
size_t len = JS_ArrayLength(js,arr);
|
||||
@@ -1463,11 +1116,6 @@ static const JSCFunctionListEntry js_geometry_funcs[] = {
|
||||
MIST_FUNC_DEF(geometry, transform_xy_blob, 2),
|
||||
MIST_FUNC_DEF(gpu, tile, 4),
|
||||
MIST_FUNC_DEF(gpu, slice9, 4),
|
||||
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),
|
||||
MIST_FUNC_DEF(gpu, make_sprite_queue, 4),
|
||||
MIST_FUNC_DEF(geometry, renderitem_push, 3),
|
||||
MIST_FUNC_DEF(geometry, renderitem_sort, 0),
|
||||
MIST_FUNC_DEF(geometry, renderitem_clear, 0),
|
||||
MIST_FUNC_DEF(geometry, array_blob, 2),
|
||||
MIST_FUNC_DEF(geometry, make_quad_indices, 1),
|
||||
MIST_FUNC_DEF(geometry, make_rect_quad, 3),
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "imgui_impl_sdlrenderer3.h"
|
||||
//#include "imgui_impl_sdlgpu3.h"
|
||||
|
||||
#include "qjs_renderer.h"
|
||||
|
||||
#include "qjs_macros.h"
|
||||
|
||||
// Forward declarations for the functions we need
|
||||
@@ -338,14 +340,17 @@ JSC_CCALL(imgui_pushid,
|
||||
JSC_CCALL(imgui_popid, ImGui::PopID(); )
|
||||
|
||||
JSC_CCALL(imgui_image,
|
||||
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
|
||||
/* SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
|
||||
ImGui::Image((ImTextureID)tex, ImVec2(100, 100), ImVec2(0,0), ImVec2(1,1));
|
||||
*/
|
||||
)
|
||||
|
||||
JSC_SCALL(imgui_imagebutton,
|
||||
/*
|
||||
SDL_Texture *tex = js2SDL_Texture(js,argv[1]);
|
||||
if (ImGui::ImageButton(str, (ImTextureID)tex, ImVec2(100, 100)))
|
||||
JS_Call(js, argv[2], JS_NULL, 0, NULL);
|
||||
*/
|
||||
)
|
||||
|
||||
JSC_CCALL(imgui_sameline, ImGui::SameLine(js2number(js, argv[0])) )
|
||||
@@ -589,7 +594,7 @@ JSC_CCALL(imgui_newframe,
|
||||
)
|
||||
|
||||
JSC_CCALL(imgui_endframe,
|
||||
ImGui::Render();
|
||||
/* ImGui::Render();
|
||||
SDL_Renderer *renderer = js2SDL_Renderer(js,argv[0]);
|
||||
|
||||
int w, h;
|
||||
@@ -598,6 +603,7 @@ JSC_CCALL(imgui_endframe,
|
||||
SDL_SetRenderLogicalPresentation(renderer,0,0,SDL_LOGICAL_PRESENTATION_DISABLED);
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer);
|
||||
SDL_SetRenderLogicalPresentation(renderer,w,h,mode);
|
||||
*/
|
||||
)
|
||||
|
||||
JSC_CCALL(imgui_wantmouse,
|
||||
@@ -609,7 +615,7 @@ JSC_CCALL(imgui_wantkeys,
|
||||
)
|
||||
|
||||
JSC_CCALL(imgui_init,
|
||||
ImGui::CreateContext();
|
||||
/* ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||
@@ -632,6 +638,7 @@ JSC_CCALL(imgui_init,
|
||||
#ifdef ENABLE_IMNODES
|
||||
ImNodes::CreateContext();
|
||||
#endif
|
||||
*/
|
||||
)
|
||||
|
||||
// ==================== IMPLOT FUNCTIONS ====================
|
||||
|
||||
@@ -601,7 +601,7 @@ JSC_CCALL(surface_constructor,
|
||||
|
||||
if (!raw) {
|
||||
JS_FreeValue(js, pixels_val);
|
||||
return JS_ThrowTypeError(js, "pixels property must be an ArrayBuffer");
|
||||
return JS_ThrowTypeError(js, "pixels property must be a stoned blob");
|
||||
}
|
||||
|
||||
// Get pitch if provided, otherwise calculate it
|
||||
@@ -658,6 +658,251 @@ static const JSCFunctionListEntry js_SDL_Surface_funcs[] = {
|
||||
JS_CGETSET_DEF("pitch", js_surface_get_pitch, NULL),
|
||||
};
|
||||
|
||||
// Helper function to create SDL_Surface from image object
|
||||
static SDL_Surface* image_to_surface(JSContext *js, JSValue img_obj)
|
||||
{
|
||||
// Get width and height
|
||||
JSValue width_val = JS_GetPropertyStr(js, img_obj, "width");
|
||||
JSValue height_val = JS_GetPropertyStr(js, img_obj, "height");
|
||||
|
||||
if (JS_IsNull(width_val) || JS_IsNull(height_val)) {
|
||||
JS_FreeValue(js, width_val);
|
||||
JS_FreeValue(js, height_val);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 NULL;
|
||||
}
|
||||
JS_FreeValue(js, width_val);
|
||||
JS_FreeValue(js, height_val);
|
||||
|
||||
// Get format
|
||||
JSValue format_val = JS_GetPropertyStr(js, img_obj, "format");
|
||||
SDL_PixelFormat format = js2pixelformat(js, format_val);
|
||||
JS_FreeValue(js, format_val);
|
||||
|
||||
if (format == SDL_PIXELFORMAT_UNKNOWN)
|
||||
format = SDL_PIXELFORMAT_RGBA32; // default
|
||||
|
||||
// Get pixels
|
||||
JSValue pixels_val = JS_GetPropertyStr(js, img_obj, "pixels");
|
||||
size_t pixel_len;
|
||||
void *pixel_data = js_get_blob_data(js, &pixel_len, pixels_val);
|
||||
|
||||
if (!pixel_data) {
|
||||
JS_FreeValue(js, pixels_val);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get pitch (optional)
|
||||
int pitch;
|
||||
JSValue pitch_val = JS_GetPropertyStr(js, img_obj, "pitch");
|
||||
if (!JS_IsNull(pitch_val)) {
|
||||
pitch = js2number(js, pitch_val);
|
||||
JS_FreeValue(js, pitch_val);
|
||||
} else {
|
||||
pitch = width * SDL_BYTESPERPIXEL(format);
|
||||
}
|
||||
|
||||
// Create a copy of pixel data since SDL_Surface will own it
|
||||
void *pixels_copy = malloc(pixel_len);
|
||||
if (!pixels_copy) {
|
||||
JS_FreeValue(js, pixels_val);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(pixels_copy, pixel_data, pixel_len);
|
||||
JS_FreeValue(js, pixels_val);
|
||||
|
||||
SDL_Surface *surface = SDL_CreateSurfaceFrom(width, height, format, pixels_copy, pitch);
|
||||
if (!surface) {
|
||||
free(pixels_copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
// Helper function to convert SDL_Surface back to image object
|
||||
static JSValue surface_to_image(JSContext *js, SDL_Surface *surf)
|
||||
{
|
||||
JSValue obj = JS_NewObject(js);
|
||||
|
||||
JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, surf->w));
|
||||
JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, surf->h));
|
||||
JS_SetPropertyStr(js, obj, "format", pixelformat2js(js, surf->format));
|
||||
JS_SetPropertyStr(js, obj, "pitch", JS_NewInt32(js, surf->pitch));
|
||||
|
||||
// Lock surface if needed
|
||||
int locked = 0;
|
||||
if (SDL_MUSTLOCK(surf)) {
|
||||
if (SDL_LockSurface(surf) < 0) {
|
||||
JS_FreeValue(js, obj);
|
||||
return JS_NULL;
|
||||
}
|
||||
locked = 1;
|
||||
}
|
||||
|
||||
// Add pixels as stoned blob
|
||||
size_t byte_size = surf->pitch * surf->h;
|
||||
JSValue pixels = js_new_blob_stoned_copy(js, surf->pixels, byte_size);
|
||||
JS_SetPropertyStr(js, obj, "pixels", pixels);
|
||||
|
||||
// Unlock if we locked
|
||||
if (locked)
|
||||
SDL_UnlockSurface(surf);
|
||||
|
||||
// Add depth and hdr properties for completeness
|
||||
JS_SetPropertyStr(js, obj, "depth", JS_NewInt32(js, SDL_BITSPERPIXEL(surf->format)));
|
||||
JS_SetPropertyStr(js, obj, "hdr", JS_FALSE);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Scale function for image objects
|
||||
JSC_CCALL(surface_scale_img,
|
||||
if (argc < 2)
|
||||
return JS_ThrowTypeError(js, "scale requires image and options objects");
|
||||
|
||||
SDL_Surface *src = image_to_surface(js, argv[0]);
|
||||
if (!src)
|
||||
return JS_ThrowTypeError(js, "First argument must be a valid image object");
|
||||
|
||||
// Get width and height from options
|
||||
JSValue width_val = JS_GetPropertyStr(js, argv[1], "width");
|
||||
JSValue height_val = JS_GetPropertyStr(js, argv[1], "height");
|
||||
|
||||
int new_width = src->w, new_height = src->h;
|
||||
if (!JS_IsNull(width_val))
|
||||
JS_ToInt32(js, &new_width, width_val);
|
||||
if (!JS_IsNull(height_val))
|
||||
JS_ToInt32(js, &new_height, height_val);
|
||||
|
||||
JS_FreeValue(js, width_val);
|
||||
JS_FreeValue(js, height_val);
|
||||
|
||||
// Get scale mode
|
||||
JSValue mode_val = JS_GetPropertyStr(js, argv[1], "mode");
|
||||
SDL_ScaleMode mode = js2SDL_ScaleMode(js, mode_val);
|
||||
JS_FreeValue(js, mode_val);
|
||||
|
||||
SDL_Surface *dst = SDL_ScaleSurface(src, new_width, new_height, mode);
|
||||
SDL_DestroySurface(src);
|
||||
|
||||
if (!dst)
|
||||
return JS_ThrowInternalError(js, "Scale failed: %s", SDL_GetError());
|
||||
|
||||
JSValue result = surface_to_image(js, dst);
|
||||
SDL_DestroySurface(dst);
|
||||
return result;
|
||||
)
|
||||
|
||||
// Fill function for image objects
|
||||
JSC_CCALL(surface_fill_img,
|
||||
if (argc < 2)
|
||||
return JS_ThrowTypeError(js, "fill requires image and color");
|
||||
|
||||
SDL_Surface *surf = image_to_surface(js, argv[0]);
|
||||
if (!surf)
|
||||
return JS_ThrowTypeError(js, "First argument must be a valid image object");
|
||||
|
||||
colorf color = js2color(js, argv[1]);
|
||||
rect r = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.w = surf->w,
|
||||
.h = surf->h
|
||||
};
|
||||
|
||||
SDL_FillSurfaceRect(surf, &r, SDL_MapRGBA(&pdetails, NULL, color.r*255, color.g*255, color.b*255, color.a*255));
|
||||
|
||||
JSValue result = surface_to_image(js, surf);
|
||||
SDL_DestroySurface(surf);
|
||||
return result;
|
||||
)
|
||||
|
||||
// Rect function for image objects
|
||||
JSC_CCALL(surface_rect_img,
|
||||
if (argc < 3)
|
||||
return JS_ThrowTypeError(js, "rect requires image, rectangle, and color");
|
||||
|
||||
SDL_Surface *surf = image_to_surface(js, argv[0]);
|
||||
if (!surf)
|
||||
return JS_ThrowTypeError(js, "First argument must be a valid image object");
|
||||
|
||||
rect r = js2rect(js, argv[1]);
|
||||
colorf color = js2color(js, argv[2]);
|
||||
|
||||
SDL_FillSurfaceRect(surf, &r, SDL_MapRGBA(&pdetails, NULL, color.r*255, color.g*255, color.b*255, color.a*255));
|
||||
|
||||
JSValue result = surface_to_image(js, surf);
|
||||
SDL_DestroySurface(surf);
|
||||
return result;
|
||||
)
|
||||
|
||||
// Blit function for image objects
|
||||
JSC_CCALL(surface_blit_img,
|
||||
if (argc < 2)
|
||||
return JS_ThrowTypeError(js, "blit requires destination and source images");
|
||||
|
||||
SDL_Surface *dst = image_to_surface(js, argv[0]);
|
||||
if (!dst)
|
||||
return JS_ThrowTypeError(js, "First argument must be a valid destination image");
|
||||
|
||||
SDL_Surface *src = image_to_surface(js, argv[1]);
|
||||
if (!src) {
|
||||
SDL_DestroySurface(dst);
|
||||
return JS_ThrowTypeError(js, "Second argument must be a valid source image");
|
||||
}
|
||||
|
||||
irect dr = {0}, *pdr = NULL;
|
||||
if (argc > 2 && !JS_IsNull(argv[2])) {
|
||||
dr = js2irect(js, argv[2]);
|
||||
pdr = &dr;
|
||||
}
|
||||
|
||||
irect sr = {0}, *psr = NULL;
|
||||
if (argc > 3 && !JS_IsNull(argv[3])) {
|
||||
sr = js2irect(js, argv[3]);
|
||||
psr = &sr;
|
||||
}
|
||||
|
||||
SDL_ScaleMode mode = SDL_SCALEMODE_LINEAR;
|
||||
if (argc > 4)
|
||||
mode = js2SDL_ScaleMode(js, argv[4]);
|
||||
|
||||
SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_NONE);
|
||||
SDL_BlitSurfaceScaled(src, psr, dst, pdr, mode);
|
||||
|
||||
SDL_DestroySurface(src);
|
||||
JSValue result = surface_to_image(js, dst);
|
||||
SDL_DestroySurface(dst);
|
||||
return result;
|
||||
)
|
||||
|
||||
// Duplicate function for image objects
|
||||
JSC_CCALL(surface_dup_img,
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(js, "dup requires an image object");
|
||||
|
||||
SDL_Surface *surf = image_to_surface(js, argv[0]);
|
||||
if (!surf)
|
||||
return JS_ThrowTypeError(js, "Argument must be a valid image object");
|
||||
|
||||
SDL_Surface *dup = SDL_DuplicateSurface(surf);
|
||||
SDL_DestroySurface(surf);
|
||||
|
||||
if (!dup)
|
||||
return JS_ThrowInternalError(js, "Duplicate failed: %s", SDL_GetError());
|
||||
|
||||
JSValue result = surface_to_image(js, dup);
|
||||
SDL_DestroySurface(dup);
|
||||
return result;
|
||||
)
|
||||
|
||||
// Generic convert function for pixel format/colorspace conversion
|
||||
JSC_CCALL(surface_convert_generic,
|
||||
if (argc < 2)
|
||||
@@ -776,9 +1021,22 @@ JSC_CCALL(surface_convert_generic,
|
||||
return JS_ThrowInternalError(js, "Pixel conversion failed: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
// Return a stoned blob with the converted pixels
|
||||
ret = js_new_blob_stoned_copy(js, dst_pixels, dst_size);
|
||||
// Create result image object
|
||||
JSValue result = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, result, "width", JS_NewInt32(js, src_width));
|
||||
JS_SetPropertyStr(js, result, "height", JS_NewInt32(js, src_height));
|
||||
JS_SetPropertyStr(js, result, "format", pixelformat2js(js, dst_format));
|
||||
JS_SetPropertyStr(js, result, "pitch", JS_NewInt32(js, dst_pitch));
|
||||
|
||||
JSValue pixels = js_new_blob_stoned_copy(js, dst_pixels, dst_size);
|
||||
free(dst_pixels);
|
||||
JS_SetPropertyStr(js, result, "pixels", pixels);
|
||||
|
||||
// Add depth and hdr for consistency
|
||||
JS_SetPropertyStr(js, result, "depth", JS_NewInt32(js, SDL_BITSPERPIXEL(dst_format)));
|
||||
JS_SetPropertyStr(js, result, "hdr", JS_FALSE);
|
||||
|
||||
return result;
|
||||
)
|
||||
|
||||
JSValue js_sdl_surface_use(JSContext *js)
|
||||
@@ -801,5 +1059,12 @@ JSValue js_sdl_surface_use(JSContext *js)
|
||||
JS_SetPropertyStr(js, ctor, "compress_bc4", JS_NewCFunction(js, js_surface_compress_bc4, "compress_bc4", 1));
|
||||
JS_SetPropertyStr(js, ctor, "compress_bc5", JS_NewCFunction(js, js_surface_compress_bc5, "compress_bc5", 1));
|
||||
|
||||
// Add standalone image manipulation functions
|
||||
JS_SetPropertyStr(js, ctor, "scale", JS_NewCFunction(js, js_surface_scale_img, "scale", 2));
|
||||
JS_SetPropertyStr(js, ctor, "fill", JS_NewCFunction(js, js_surface_fill_img, "fill", 2));
|
||||
JS_SetPropertyStr(js, ctor, "rect", JS_NewCFunction(js, js_surface_rect_img, "rect", 3));
|
||||
JS_SetPropertyStr(js, ctor, "blit", JS_NewCFunction(js, js_surface_blit_img, "blit", 5));
|
||||
JS_SetPropertyStr(js, ctor, "dup", JS_NewCFunction(js, js_surface_dup_img, "dup", 1));
|
||||
|
||||
return ctor;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
128
source/qjs_staef.c
Normal file
128
source/qjs_staef.c
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "qjs_staef.h"
|
||||
#include "font.h"
|
||||
#include "qjs_macros.h"
|
||||
#include "HandmadeMath.h"
|
||||
#include "render.h"
|
||||
#include "stb_ds.h"
|
||||
#include "cell.h"
|
||||
#include "qjs_sdl.h"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
// External functions from jsffi.c
|
||||
typedef HMM_Vec4 colorf;
|
||||
extern colorf js2color(JSContext *js, JSValue v);
|
||||
extern JSValue vec22js(JSContext *js, HMM_Vec2 v);
|
||||
extern HMM_Vec2 js2vec2(JSContext *js, JSValue v);
|
||||
extern rect js2rect(JSContext *js, JSValue v);
|
||||
extern double js2number(JSContext *js, JSValue v);
|
||||
extern JSValue number2js(JSContext *js, double g);
|
||||
extern void *js_get_blob_data(JSContext *js, size_t *len, JSValue v);
|
||||
extern JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t size);
|
||||
extern JSValue quads_to_mesh(JSContext *js, text_vert *buffer);
|
||||
extern int uncaught_exception(JSContext *js, JSValue ret);
|
||||
|
||||
// QuickJS class for font
|
||||
QJSCLASS(font,)
|
||||
|
||||
// Font constructor
|
||||
JSC_CCALL(staef_font_new,
|
||||
size_t len;
|
||||
void *data = js_get_blob_data(js, &len, argv[0]);
|
||||
if (!data) return JS_ThrowReferenceError(js, "could not get array buffer data");
|
||||
|
||||
double height = js2number(js, argv[1]);
|
||||
font *f = MakeFont(data, len, (int)height);
|
||||
if (!f) return JS_ThrowReferenceError(js, "could not create font");
|
||||
|
||||
ret = font2js(js, f);
|
||||
|
||||
// Create surface data object for the font's atlas
|
||||
if (f->surface) {
|
||||
JSValue surfData = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, f->surface->w));
|
||||
JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, f->surface->h));
|
||||
JS_SetPropertyStr(js, surfData, "format", pixelformat2js(js, f->surface->format));
|
||||
JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, f->surface->pitch));
|
||||
|
||||
// Lock surface if needed
|
||||
int locked = 0;
|
||||
if (SDL_MUSTLOCK(f->surface)) {
|
||||
if (SDL_LockSurface(f->surface) < 0)
|
||||
return JS_ThrowInternalError(js, "Lock surface failed: %s", SDL_GetError());
|
||||
locked = 1;
|
||||
}
|
||||
|
||||
size_t byte_size = f->surface->pitch * f->surface->h;
|
||||
JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, f->surface->pixels, byte_size));
|
||||
|
||||
if (locked)
|
||||
SDL_UnlockSurface(f->surface);
|
||||
|
||||
JS_SetPropertyStr(js, ret, "surface", surfData);
|
||||
}
|
||||
)
|
||||
|
||||
// Calculate text size
|
||||
JSC_CCALL(staef_font_text_size,
|
||||
font *f = js2font(js, self);
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
float letterSpacing = argc > 1 ? js2number(js, argv[1]) : 0;
|
||||
float wrap = argc > 2 ? js2number(js, argv[2]) : -1;
|
||||
|
||||
ret = vec22js(js, measure_text(str, f, letterSpacing, wrap));
|
||||
JS_FreeCString(js, str);
|
||||
)
|
||||
|
||||
// Generate text buffer (mesh data)
|
||||
JSC_CCALL(staef_font_make_text_buffer,
|
||||
font *f = js2font(js, self);
|
||||
const char *s = JS_ToCString(js, argv[0]);
|
||||
rect rectpos = js2rect(js, argv[1]);
|
||||
colorf c = js2color(js, argv[2]);
|
||||
float wrap = argc > 3 ? js2number(js, argv[3]) : -1;
|
||||
|
||||
HMM_Vec2 startpos = {.x = rectpos.x, .y = rectpos.y };
|
||||
text_vert *buffer = renderText(s, startpos, f, c, wrap);
|
||||
ret = quads_to_mesh(js, buffer);
|
||||
JS_FreeCString(js, s);
|
||||
arrfree(buffer);
|
||||
)
|
||||
|
||||
// Font property getters/setters
|
||||
JSC_GETSET(font, linegap, number)
|
||||
|
||||
// Font methods
|
||||
static const JSCFunctionListEntry js_font_funcs[] = {
|
||||
MIST_FUNC_DEF(staef_font, text_size, 3),
|
||||
MIST_FUNC_DEF(staef_font, make_text_buffer, 4),
|
||||
CGETSET_ADD(font, linegap),
|
||||
};
|
||||
|
||||
// Font constructor function
|
||||
static JSValue js_font_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv)
|
||||
{
|
||||
return js_staef_font_new(ctx, JS_NULL, argc, argv);
|
||||
}
|
||||
|
||||
// Initialize the staef module
|
||||
JSValue js_staef_use(JSContext *js)
|
||||
{
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JSValue proto;
|
||||
|
||||
// Initialize font class
|
||||
JS_NewClassID(&js_font_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_font_id, &js_font_class);
|
||||
proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, proto, js_font_funcs, countof(js_font_funcs));
|
||||
JS_SetClassProto(js, js_font_id, proto);
|
||||
|
||||
// Create font constructor
|
||||
JSValue font_ctor = JS_NewCFunction2(js, js_font_constructor, "font", 2, JS_CFUNC_constructor, 0);
|
||||
JS_SetConstructor(js, font_ctor, proto);
|
||||
|
||||
// Add font constructor to module (lowercase to match "new staef.font")
|
||||
JS_SetPropertyStr(js, mod, "font", font_ctor);
|
||||
|
||||
return mod;
|
||||
}
|
||||
8
source/qjs_staef.h
Normal file
8
source/qjs_staef.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef QJS_STAEF_H
|
||||
#define QJS_STAEF_H
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
JSValue js_staef_use(JSContext *js);
|
||||
|
||||
#endif // QJS_STAEF_H
|
||||
@@ -427,7 +427,7 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
|
||||
run = 0;
|
||||
}
|
||||
|
||||
index_pos = QOI_COLOR_HASH(px) % 64;
|
||||
index_pos = QOI_COLOR_HASH(px) & (64 - 1);
|
||||
|
||||
if (index[index_pos].v == px.v) {
|
||||
bytes[p++] = QOI_OP_INDEX | index_pos;
|
||||
@@ -574,7 +574,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
|
||||
run = (b1 & 0x3f);
|
||||
}
|
||||
|
||||
index[QOI_COLOR_HASH(px) % 64] = px;
|
||||
index[QOI_COLOR_HASH(px) & (64 - 1)] = px;
|
||||
}
|
||||
|
||||
pixels[px_pos + 0] = px.rgba.r;
|
||||
|
||||
@@ -136,7 +136,7 @@ $_.receiver(e => {
|
||||
case "drop_file":
|
||||
log.console(`got ${e.data} dropped`)
|
||||
var data = io.slurpbytes(e.data)
|
||||
img = graphics.make_texture(data)
|
||||
img = graphics.image_decode(data)
|
||||
var qr_surf = img;//extract_qr_surface(img)
|
||||
var qr_surf_scaled = qr_surf.scale([qr_surf.width/4, qr_surf.height/4])
|
||||
var image = {surface:qr_surf_scaled}
|
||||
|
||||
Reference in New Issue
Block a user