move surface to its own module
This commit is contained in:
@@ -195,7 +195,7 @@ sources = []
|
|||||||
src += [
|
src += [
|
||||||
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
||||||
'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c',
|
'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c',
|
||||||
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_video.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_os.c', 'qjs_actor.c',
|
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_os.c', 'qjs_actor.c',
|
||||||
'qjs_qr.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'
|
'qjs_qr.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'
|
||||||
]
|
]
|
||||||
# quirc src
|
# quirc src
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// SDL Video Actor
|
// SDL Video Actor
|
||||||
// This actor runs on the main thread and handles all SDL video operations
|
// This actor runs on the main thread and handles all SDL video operations
|
||||||
|
|
||||||
|
var surface = use('surface')
|
||||||
|
|
||||||
// Default window configuration - documents all available window options
|
// Default window configuration - documents all available window options
|
||||||
var default_window = {
|
var default_window = {
|
||||||
// Basic properties
|
// Basic properties
|
||||||
@@ -431,11 +433,11 @@ function handle_renderer(msg) {
|
|||||||
}
|
}
|
||||||
// Load from raw surface object (for graphics module)
|
// Load from raw surface object (for graphics module)
|
||||||
else if (msg.data.surface) {
|
else if (msg.data.surface) {
|
||||||
tex = ren.load_texture(msg.data);
|
tex = ren.load_texture(new surface(msg.data));
|
||||||
}
|
}
|
||||||
// Direct surface data
|
// Direct surface data
|
||||||
else if (msg.data.width && msg.data.height) {
|
else if (msg.data.width && msg.data.height) {
|
||||||
tex = ren.load_texture(msg.data);
|
tex = ren.load_texture(new surface(msg.data));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return {error: "Must provide surface_id or surface data"};
|
return {error: "Must provide surface_id or surface data"};
|
||||||
@@ -446,25 +448,8 @@ function handle_renderer(msg) {
|
|||||||
resources.texture[tex_id] = tex;
|
resources.texture[tex_id] = tex;
|
||||||
return {
|
return {
|
||||||
id: tex_id,
|
id: tex_id,
|
||||||
width: tex.width,
|
|
||||||
height: tex.height
|
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'createTexture':
|
|
||||||
if (!msg.data || !msg.data.width || !msg.data.height) {
|
|
||||||
return {error: "Missing width or height"};
|
|
||||||
}
|
|
||||||
var tex = ren.createTexture(
|
|
||||||
msg.data.format || 'rgba8888',
|
|
||||||
msg.data.access || 'static',
|
|
||||||
msg.data.width,
|
|
||||||
msg.data.height
|
|
||||||
);
|
|
||||||
if (!tex) return {error: "Failed to create texture"};
|
|
||||||
var tex_id = allocate_id();
|
|
||||||
resources.texture[tex_id] = tex;
|
|
||||||
return {id: tex_id, data: {size: tex.size}};
|
|
||||||
|
|
||||||
case 'flush':
|
case 'flush':
|
||||||
ren.flush();
|
ren.flush();
|
||||||
return {success: true};
|
return {success: true};
|
||||||
@@ -539,6 +524,7 @@ function handle_texture(msg) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
console.log(json.encode(msg.data))
|
||||||
return {error: "Must provide either surface_id or width/height"};
|
return {error: "Must provide either surface_id or width/height"};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ var wota = hidden.wota
|
|||||||
var console_mod = hidden.console
|
var console_mod = hidden.console
|
||||||
var use_embed = hidden.use_embed
|
var use_embed = hidden.use_embed
|
||||||
var use_dyn = hidden.use_dyn
|
var use_dyn = hidden.use_dyn
|
||||||
|
var enet = hidden.enet
|
||||||
|
var nota = hidden.nota
|
||||||
|
|
||||||
// Strip hidden from prosperon so nothing else can access it
|
// Strip hidden from prosperon so nothing else can access it
|
||||||
delete prosperon.hidden
|
delete prosperon.hidden
|
||||||
@@ -150,37 +152,21 @@ console.error = function error(e) {
|
|||||||
if (e instanceof Error)
|
if (e instanceof Error)
|
||||||
pprint(`${e.name} : ${e.message}
|
pprint(`${e.name} : ${e.message}
|
||||||
${e.stack}`, 4)
|
${e.stack}`, 4)
|
||||||
else
|
else {
|
||||||
pprint(e,4)
|
var stack = new Error()
|
||||||
|
pprint(`${e}
|
||||||
|
${stack.stack}`,4)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
console.panic = function panic(e) {
|
console.panic = function panic(e) {
|
||||||
pprint(e, 5)
|
pprint(e, 5)
|
||||||
os.quit()
|
os.quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
console.assert = function assert(op, str = `assertion failed [value '${op}']`) {
|
console.assert = function assert(op, str = `assertion failed [value '${op}']`) {
|
||||||
if (!op) console.panic(str)
|
if (!op) console.panic(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
//os.on('uncaught_exception', function(e) { console.error(e); })
|
|
||||||
|
|
||||||
console[prosperon.DOC] = {
|
|
||||||
doc: "The console object provides various logging, debugging, and output methods.",
|
|
||||||
spam: "Output a spam-level message for very verbose logging.",
|
|
||||||
debug: "Output a debug-level message.",
|
|
||||||
info: "Output info level message.",
|
|
||||||
warn: "Output warn level message.",
|
|
||||||
error: "Output error level message, and print stacktrace.",
|
|
||||||
panic: "Output a panic-level message and exit the program.",
|
|
||||||
assert: "If the condition is false, print an error and panic.",
|
|
||||||
critical: "Output critical level message, and exit game immediately.",
|
|
||||||
write: "Write raw text to console.",
|
|
||||||
say: "Write raw text to console, plus a newline.",
|
|
||||||
log: "Output directly to in game console.",
|
|
||||||
level: "Set level to output logging to console.",
|
|
||||||
stack: "Output a stacktrace to console.",
|
|
||||||
clear: "Clear console."
|
|
||||||
}
|
|
||||||
|
|
||||||
var BASEPATH = 'scripts/core/base.js'
|
var BASEPATH = 'scripts/core/base.js'
|
||||||
var script = io.slurp(BASEPATH)
|
var script = io.slurp(BASEPATH)
|
||||||
var fnname = "base"
|
var fnname = "base"
|
||||||
@@ -316,11 +302,9 @@ script = `(function ${fnname}() { ${script}; })`
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var enet = use('enet')
|
|
||||||
var util = use('util')
|
var util = use('util')
|
||||||
var math = use('math')
|
var math = use('math')
|
||||||
var crypto = use('crypto')
|
var crypto = use('crypto')
|
||||||
var nota = use('nota')
|
|
||||||
|
|
||||||
var dying = false
|
var dying = false
|
||||||
|
|
||||||
|
|||||||
@@ -505,10 +505,10 @@ draw.image = function image(image, rect = [0,0], rotation = 0, anchor = [0,0], s
|
|||||||
|
|
||||||
// Calculate source rectangle from image.rect (UV coords)
|
// Calculate source rectangle from image.rect (UV coords)
|
||||||
var src_rect = {
|
var src_rect = {
|
||||||
x: image.rect.x * texture.width,
|
x: image.rect.x * image.width,
|
||||||
y: image.rect.y * texture.height,
|
y: image.rect.y * image.height,
|
||||||
width: image.rect.width * texture.width,
|
width: image.rect.width * image.width,
|
||||||
height: image.rect.height * texture.height
|
height: image.rect.height * image.height
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle flipping
|
// Handle flipping
|
||||||
|
|||||||
@@ -21,65 +21,84 @@ var LOADING = Symbol()
|
|||||||
|
|
||||||
var cache = new Map()
|
var cache = new Map()
|
||||||
|
|
||||||
// When creating an image, do an Object.create(graphics.Image)
|
// Image constructor function
|
||||||
graphics.Image = {
|
graphics.Image = function(surfaceData) {
|
||||||
get gpu() {
|
// Initialize private properties
|
||||||
this[LASTUSE] = os.now();
|
this[CPU] = surfaceData || undefined;
|
||||||
if (!this[GPU] && !this[LOADING]) {
|
this[GPU] = undefined;
|
||||||
this[LOADING] = true;
|
this[LOADING] = false;
|
||||||
var self = this;
|
this[LASTUSE] = os.now();
|
||||||
|
this.rect = {x:0, y:0, width:1, height:1};
|
||||||
// Send message to load texture
|
}
|
||||||
send(renderer_actor, {
|
|
||||||
kind: "renderer",
|
|
||||||
id: renderer_id,
|
|
||||||
op: "loadTexture",
|
|
||||||
data: this[CPU]
|
|
||||||
}, function(response) {
|
|
||||||
if (response.error) {
|
|
||||||
console.error("Failed to load texture:", response.error);
|
|
||||||
self[LOADING] = false;
|
|
||||||
} else {
|
|
||||||
self[GPU] = response;
|
|
||||||
decorate_rect_px(self);
|
|
||||||
self[LOADING] = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this[GPU]
|
|
||||||
},
|
|
||||||
|
|
||||||
get texture() { return this.gpu },
|
|
||||||
|
|
||||||
get cpu() {
|
// Define getters and methods on the prototype
|
||||||
this[LASTUSE] = os.now();
|
Object.defineProperties(graphics.Image.prototype, {
|
||||||
// Note: Reading texture back from GPU requires async operation
|
gpu: {
|
||||||
// For now, return the CPU data if available
|
get: function() {
|
||||||
return this[CPU]
|
this[LASTUSE] = os.now();
|
||||||
|
if (!this[GPU] && !this[LOADING]) {
|
||||||
|
this[LOADING] = true;
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Send message to load texture
|
||||||
|
send(renderer_actor, {
|
||||||
|
kind: "renderer",
|
||||||
|
id: renderer_id,
|
||||||
|
op: "loadTexture",
|
||||||
|
data: this[CPU]
|
||||||
|
}, function(response) {
|
||||||
|
if (response.error) {
|
||||||
|
console.error("Failed to load texture:", response.error)
|
||||||
|
self[LOADING] = false;
|
||||||
|
} else {
|
||||||
|
self[GPU] = response;
|
||||||
|
decorate_rect_px(self);
|
||||||
|
self[LOADING] = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this[GPU]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get surface() { return this.cpu },
|
texture: {
|
||||||
|
get: function() { return this.gpu }
|
||||||
get width() {
|
},
|
||||||
if (this[GPU]) return this[GPU].width
|
|
||||||
if (this[CPU]) return this[CPU].width
|
cpu: {
|
||||||
return 0
|
get: function() {
|
||||||
|
this[LASTUSE] = os.now();
|
||||||
|
// Note: Reading texture back from GPU requires async operation
|
||||||
|
// For now, return the CPU data if available
|
||||||
|
return this[CPU]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get height() {
|
surface: {
|
||||||
if (this[GPU]) return this[GPU].height
|
get: function() { return this.cpu }
|
||||||
if (this[CPU]) return this[CPU].height
|
|
||||||
return 0
|
|
||||||
},
|
},
|
||||||
|
|
||||||
unload_gpu() {
|
width: {
|
||||||
this[GPU] = undefined
|
get: function() {
|
||||||
|
return this[CPU].width
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
unload_cpu() {
|
height: {
|
||||||
this[CPU] = undefined
|
get: function() {
|
||||||
},
|
return this[CPU].height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add methods to prototype
|
||||||
|
graphics.Image.prototype.unload_gpu = function() {
|
||||||
|
this[GPU] = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.Image.prototype.unload_cpu = function() {
|
||||||
|
this[CPU] = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
function calc_image_size(img) {
|
function calc_image_size(img) {
|
||||||
@@ -105,13 +124,7 @@ function decorate_rect_px(img) {
|
|||||||
|
|
||||||
function make_handle(obj)
|
function make_handle(obj)
|
||||||
{
|
{
|
||||||
return Object.assign(Object.create(graphics.Image), {
|
return new graphics.Image(obj);
|
||||||
rect:{x:0,y:0,width:1,height:1},
|
|
||||||
[CPU]:obj,
|
|
||||||
[GPU]:undefined,
|
|
||||||
[LOADING]:false,
|
|
||||||
[LASTUSE]:os.now()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapSurface(surf, maybeRect){
|
function wrapSurface(surf, maybeRect){
|
||||||
@@ -144,7 +157,7 @@ function create_image(path){
|
|||||||
try{
|
try{
|
||||||
const bytes = io.slurpbytes(path);
|
const bytes = io.slurpbytes(path);
|
||||||
let raw = decode_image(bytes, path.ext());
|
let raw = decode_image(bytes, path.ext());
|
||||||
|
|
||||||
/* ── Case A: static image ─────────────────────────────────── */
|
/* ── Case A: static image ─────────────────────────────────── */
|
||||||
if(raw.surface)
|
if(raw.surface)
|
||||||
return make_handle(raw.surface);
|
return make_handle(raw.surface);
|
||||||
@@ -210,10 +223,9 @@ graphics.texture_from_data = function(data)
|
|||||||
{
|
{
|
||||||
if (!(data instanceof ArrayBuffer)) return undefined
|
if (!(data instanceof ArrayBuffer)) return undefined
|
||||||
|
|
||||||
var surface = graphics.make_texture(data);
|
var image = graphics.make_texture(data);
|
||||||
var img = make_handle(surface);
|
var img = make_handle(image)
|
||||||
|
|
||||||
// Trigger GPU load (async)
|
|
||||||
img.gpu;
|
img.gpu;
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
@@ -234,7 +246,7 @@ graphics.from = function(id, data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
graphics.texture = function texture(path) {
|
graphics.texture = function texture(path) {
|
||||||
if (path.__proto__ === graphics.Image) return path
|
if (path instanceof graphics.Image) return path
|
||||||
|
|
||||||
if (typeof path !== 'string')
|
if (typeof path !== 'string')
|
||||||
throw new Error('need a string for graphics.texture')
|
throw new Error('need a string for graphics.texture')
|
||||||
@@ -243,7 +255,8 @@ graphics.texture = function texture(path) {
|
|||||||
if (cache.has(id)) return cache.get(id)
|
if (cache.has(id)) return cache.get(id)
|
||||||
|
|
||||||
var ipath = res.find_image(id)
|
var ipath = res.find_image(id)
|
||||||
if (!ipath) throw new Error(`unknown image ${id}`)
|
if (!ipath)
|
||||||
|
throw new Error(`unknown image ${id}`)
|
||||||
|
|
||||||
var image = create_image(ipath)
|
var image = create_image(ipath)
|
||||||
cache.set(id, image)
|
cache.set(id, image)
|
||||||
@@ -340,8 +353,6 @@ graphics.get_font = function get_font(path, size) {
|
|||||||
console.error("Failed to load font texture:", response.error);
|
console.error("Failed to load font texture:", response.error);
|
||||||
} else {
|
} else {
|
||||||
font.texture = response;
|
font.texture = response;
|
||||||
console.log('loaded font texture');
|
|
||||||
console.log(json.encode(font.texture));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -418,12 +429,6 @@ graphics.cull_sprites[prosperon.DOC] = `
|
|||||||
Filter an array of sprites to only those visible in the provided camera’s view.
|
Filter an array of sprites to only those visible in the provided camera’s view.
|
||||||
`
|
`
|
||||||
|
|
||||||
graphics.make_surface[prosperon.DOC] = `
|
|
||||||
:param dimensions: The size object {width, height}, or an array [w,h].
|
|
||||||
:return: A blank RGBA surface with the given dimensions, typically for software rendering or icons.
|
|
||||||
Create a blank surface in RAM.
|
|
||||||
`
|
|
||||||
|
|
||||||
graphics.make_cursor[prosperon.DOC] = `
|
graphics.make_cursor[prosperon.DOC] = `
|
||||||
:param opts: An object with {surface, hotx, hoty} or similar.
|
:param opts: An object with {surface, hotx, hoty} or similar.
|
||||||
:return: An SDL_Cursor object referencing the given surface for a custom mouse cursor.
|
:return: An SDL_Cursor object referencing the given surface for a custom mouse cursor.
|
||||||
|
|||||||
373
source/jsffi.c
373
source/jsffi.c
@@ -48,6 +48,7 @@
|
|||||||
#include "qjs_spline.h"
|
#include "qjs_spline.h"
|
||||||
#include "qjs_js.h"
|
#include "qjs_js.h"
|
||||||
#include "qjs_debug.h"
|
#include "qjs_debug.h"
|
||||||
|
#include "qjs_sdl_surface.h"
|
||||||
#ifndef NSTEAM
|
#ifndef NSTEAM
|
||||||
#include "qjs_steam.h"
|
#include "qjs_steam.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -375,11 +376,6 @@ char *js2strdup(JSContext *js, JSValue v) {
|
|||||||
#include "qjs_macros.h"
|
#include "qjs_macros.h"
|
||||||
|
|
||||||
|
|
||||||
void SDL_Surface_free(JSRuntime *rt, SDL_Surface *s) {
|
|
||||||
if (s->flags & SDL_SURFACE_PREALLOCATED)
|
|
||||||
free(s->pixels);
|
|
||||||
SDL_DestroySurface(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c)
|
void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c)
|
||||||
{
|
{
|
||||||
@@ -389,11 +385,6 @@ void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c)
|
|||||||
QJSCLASS(font,)
|
QJSCLASS(font,)
|
||||||
QJSCLASS(datastream,)
|
QJSCLASS(datastream,)
|
||||||
|
|
||||||
QJSCLASS(SDL_Surface,
|
|
||||||
JS_SetProperty(js, j, width_atom, number2js(js,n->w));
|
|
||||||
JS_SetProperty(js,j,height_atom,number2js(js,n->h));
|
|
||||||
)
|
|
||||||
|
|
||||||
JSValue angle2js(JSContext *js,double g) {
|
JSValue angle2js(JSContext *js,double g) {
|
||||||
return number2js(js,g*HMM_RadToTurn);
|
return number2js(js,g*HMM_RadToTurn);
|
||||||
}
|
}
|
||||||
@@ -994,178 +985,6 @@ static SDL_PixelFormat js2pixelformat(JSContext *js, JSValue v)
|
|||||||
return it->fmt;
|
return it->fmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct { const char *name; SDL_ScaleMode mode; } scale_entry;
|
|
||||||
|
|
||||||
static const scale_entry k_scale_table[] = {
|
|
||||||
{ "nearest", SDL_SCALEMODE_NEAREST },
|
|
||||||
{ "linear", SDL_SCALEMODE_LINEAR },
|
|
||||||
{ NULL, SDL_SCALEMODE_LINEAR } /* fallback */
|
|
||||||
};
|
|
||||||
|
|
||||||
static JSValue scalemode2js(JSContext *js, SDL_ScaleMode mode){
|
|
||||||
const scale_entry *it;
|
|
||||||
for(it = k_scale_table; it->name; ++it)
|
|
||||||
if(it->mode == mode) break;
|
|
||||||
return JS_NewString(js, it->name ? it->name : "nearest");
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_ScaleMode js2SDL_ScaleMode(JSContext *js, JSValue v){
|
|
||||||
if(JS_IsUndefined(v)) return SDL_SCALEMODE_NEAREST;
|
|
||||||
const char *s = JS_ToCString(js, v);
|
|
||||||
if(!s) return SDL_SCALEMODE_NEAREST;
|
|
||||||
const scale_entry *it;
|
|
||||||
for(it = k_scale_table; it->name; ++it)
|
|
||||||
if(!strcmp(it->name, s)) break;
|
|
||||||
JS_FreeCString(js, s);
|
|
||||||
return it->mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------
|
|
||||||
* surface.blit(dstrect, src, srcrect, scalemode)
|
|
||||||
* -------------------------------------------------------------------------*/
|
|
||||||
JSC_CCALL(surface_blit,
|
|
||||||
SDL_Surface *dst = js2SDL_Surface(js, self);
|
|
||||||
|
|
||||||
irect dr = {0}, *pdr = NULL;
|
|
||||||
if(!JS_IsUndefined(argv[0])){ dr = js2irect(js, argv[0]); pdr = &dr; }
|
|
||||||
|
|
||||||
SDL_Surface *src = js2SDL_Surface(js, argv[1]);
|
|
||||||
if (!src)
|
|
||||||
return JS_ThrowReferenceError(js, "Argument must be a surface.");
|
|
||||||
|
|
||||||
irect sr = {0}, *psr = NULL;
|
|
||||||
if(!JS_IsUndefined(argv[2])){ sr = js2irect(js, argv[2]); psr = &sr; }
|
|
||||||
|
|
||||||
SDL_ScaleMode mode = js2SDL_ScaleMode(js, argv[3]);
|
|
||||||
|
|
||||||
SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_NONE);
|
|
||||||
|
|
||||||
printf("src %p, dst %p, src rect %g,%g,%g,%g [%p], dst rect %g,%g,%g,%g [%p]\n", src, dst, sr.x, sr.y, sr.w, sr.h, psr, dr.x, dr.y, dr.w, dr.h, pdr);
|
|
||||||
|
|
||||||
SDL_BlitSurfaceScaled(src, psr, dst, pdr, mode);
|
|
||||||
)
|
|
||||||
|
|
||||||
JSC_CCALL(surface_scale,
|
|
||||||
SDL_Surface *src = js2SDL_Surface(js,self);
|
|
||||||
HMM_Vec2 wh = js2vec2(js,argv[0]);
|
|
||||||
SDL_ScaleMode mode = js2SDL_ScaleMode(js, argv[1]);
|
|
||||||
SDL_Surface *new = SDL_ScaleSurface(src, wh.x, wh.y, mode);
|
|
||||||
ret = SDL_Surface2js(js,new);
|
|
||||||
)
|
|
||||||
|
|
||||||
static SDL_PixelFormatDetails pdetails = {
|
|
||||||
.format = SDL_PIXELFORMAT_RGBA8888, // Standard RGBA8888 format
|
|
||||||
.bits_per_pixel = 32, // 8 bits per channel, 4 channels = 32 bits
|
|
||||||
.bytes_per_pixel = 4, // 4 bytes per pixel
|
|
||||||
.padding = {0, 0}, // Unused padding
|
|
||||||
.Rmask = 0xFF000000, // Red mask
|
|
||||||
.Gmask = 0x00FF0000, // Green mask
|
|
||||||
.Bmask = 0x0000FF00, // Blue mask
|
|
||||||
.Amask = 0x000000FF, // Alpha mask
|
|
||||||
.Rbits = 8, // 8 bits for Red
|
|
||||||
.Gbits = 8, // 8 bits for Green
|
|
||||||
.Bbits = 8, // 8 bits for Blue
|
|
||||||
.Abits = 8, // 8 bits for Alpha
|
|
||||||
.Rshift = 24, // Red shift
|
|
||||||
.Gshift = 16, // Green shift
|
|
||||||
.Bshift = 8, // Blue shift
|
|
||||||
.Ashift = 0 // Alpha shift
|
|
||||||
};
|
|
||||||
|
|
||||||
JSC_CCALL(surface_fill,
|
|
||||||
SDL_Surface *src = js2SDL_Surface(js,self);
|
|
||||||
colorf color = js2color(js,argv[0]);
|
|
||||||
rect r = {
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.w = src->w,
|
|
||||||
.h = src->h
|
|
||||||
};
|
|
||||||
SDL_FillSurfaceRect(src, &r, SDL_MapRGBA(&pdetails, NULL, color.r*255,color.g*255,color.b*255,color.a*255));
|
|
||||||
)
|
|
||||||
|
|
||||||
JSC_CCALL(surface_rect,
|
|
||||||
SDL_Surface *dst = js2SDL_Surface(js,self);
|
|
||||||
rect r = js2rect(js,argv[0]);
|
|
||||||
colorf color = js2color(js,argv[1]);
|
|
||||||
SDL_FillSurfaceRect(dst,&r,SDL_MapRGBA(&pdetails,NULL, color.r*255,color.g*255,color.b*255,color.a*255));
|
|
||||||
)
|
|
||||||
|
|
||||||
JSC_CCALL(surface_convert,
|
|
||||||
SDL_Surface *surf = js2SDL_Surface(js,self);
|
|
||||||
SDL_PixelFormat fmt = js2pixelformat(js, argv[0]);
|
|
||||||
SDL_Surface *dst = SDL_ConvertSurface(surf, fmt);
|
|
||||||
if (!dst) return JS_ThrowInternalError(js, "Convert failed: %s", SDL_GetError());
|
|
||||||
|
|
||||||
return SDL_Surface2js(js, dst);
|
|
||||||
)
|
|
||||||
|
|
||||||
JSC_CCALL(surface_dup,
|
|
||||||
SDL_Surface *surf = js2SDL_Surface(js,self);
|
|
||||||
SDL_Surface *conv = SDL_DuplicateSurface(surf);
|
|
||||||
if (!conv)
|
|
||||||
return JS_ThrowReferenceError(js, "could not blit to dup'd surface: %s", SDL_GetError());
|
|
||||||
|
|
||||||
return SDL_Surface2js(js,conv);
|
|
||||||
)
|
|
||||||
|
|
||||||
JSC_CCALL(surface_pixels,
|
|
||||||
SDL_Surface *surf = js2SDL_Surface(js,self);
|
|
||||||
|
|
||||||
/* if (SDL_ISPIXELFORMAT_FOURCC(surf->format->format))
|
|
||||||
return JS_ThrowTypeError(js, "planar or FOURCC formats are not supported - convert first");
|
|
||||||
*/
|
|
||||||
|
|
||||||
int locked = 0;
|
|
||||||
if (SDL_MUSTLOCK(surf)) {
|
|
||||||
if (SDL_LockSurface(surf) < 0)
|
|
||||||
return JS_ThrowInternalError(js, "Lock surface failed: %s", SDL_GetError());
|
|
||||||
|
|
||||||
locked = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t byte_size = surf->pitch*surf->h;
|
|
||||||
|
|
||||||
ret = JS_NewArrayBufferCopy(js, surf->pixels, byte_size);
|
|
||||||
|
|
||||||
if (locked)
|
|
||||||
SDL_UnlockSurface(surf);
|
|
||||||
)
|
|
||||||
|
|
||||||
JSC_CCALL(surface_get_width,
|
|
||||||
SDL_Surface *s = js2SDL_Surface(js,self);
|
|
||||||
return JS_NewFloat64(js, s->w);
|
|
||||||
)
|
|
||||||
|
|
||||||
JSC_CCALL(surface_get_height,
|
|
||||||
SDL_Surface *s = js2SDL_Surface(js,self);
|
|
||||||
return JS_NewFloat64(js, s->h);
|
|
||||||
)
|
|
||||||
|
|
||||||
JSC_CCALL(surface_get_format,
|
|
||||||
SDL_Surface *s = js2SDL_Surface(js,self);
|
|
||||||
return pixelformat2js(js, s->format);
|
|
||||||
)
|
|
||||||
|
|
||||||
JSC_CCALL(surface_get_pitch,
|
|
||||||
SDL_Surface *s = js2SDL_Surface(js,self);
|
|
||||||
return JS_NewFloat64(js, s->pitch);
|
|
||||||
)
|
|
||||||
|
|
||||||
static const JSCFunctionListEntry js_SDL_Surface_funcs[] = {
|
|
||||||
MIST_FUNC_DEF(surface, blit, 4),
|
|
||||||
MIST_FUNC_DEF(surface, scale, 1),
|
|
||||||
MIST_FUNC_DEF(surface, fill,1),
|
|
||||||
MIST_FUNC_DEF(surface, rect,2),
|
|
||||||
MIST_FUNC_DEF(surface, dup, 0),
|
|
||||||
MIST_FUNC_DEF(surface, pixels, 0),
|
|
||||||
MIST_FUNC_DEF(surface, convert, 1),
|
|
||||||
JS_CGETSET_DEF("width", js_surface_get_width, NULL),
|
|
||||||
JS_CGETSET_DEF("height", js_surface_get_height, NULL),
|
|
||||||
JS_CGETSET_DEF("format", js_surface_get_format, NULL),
|
|
||||||
JS_CGETSET_DEF("pitch", js_surface_get_pitch, NULL),
|
|
||||||
};
|
|
||||||
|
|
||||||
JSC_CCALL(os_guid,
|
JSC_CCALL(os_guid,
|
||||||
SDL_GUID guid;
|
SDL_GUID guid;
|
||||||
randombytes(guid.data, 16);
|
randombytes(guid.data, 16);
|
||||||
@@ -1240,44 +1059,20 @@ JSC_CCALL(os_make_texture,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int pitch = width*4;
|
int pitch = width*4;
|
||||||
int fmt = SDL_PIXELFORMAT_RGBA32;
|
size_t pixels_size = pitch * height;
|
||||||
|
|
||||||
SDL_Surface *surf = SDL_CreateSurfaceFrom(width,height,fmt, data, pitch);
|
// Create JS object with surface data
|
||||||
if (!surf) {
|
JSValue obj = JS_NewObject(js);
|
||||||
free(data);
|
JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, width));
|
||||||
return JS_ThrowReferenceError(js, "Error creating surface from data: %s",SDL_GetError());
|
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));
|
||||||
ret = SDL_Surface2js(js,surf);
|
JS_SetPropertyStr(js, obj, "pixels", JS_NewArrayBufferCopy(js, data, pixels_size));
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
ret = obj;
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(graphics_surface_from_pixels,
|
|
||||||
int w, h, pitch;
|
|
||||||
SDL_PixelFormat fmt;
|
|
||||||
JS_GETATOM(js, w, argv[0], width, number)
|
|
||||||
JS_GETATOM(js, h, argv[0], height, number)
|
|
||||||
JS_GETATOM(js, fmt, argv[0], format, pixelformat)
|
|
||||||
JS_GETATOM(js, pitch, argv[0], pitch, number)
|
|
||||||
JSValue js_px = JS_GetPropertyStr(js, argv[0], "buffer");
|
|
||||||
|
|
||||||
size_t len;
|
|
||||||
void *raw = JS_GetArrayBuffer(js, &len, js_px);
|
|
||||||
|
|
||||||
JS_FreeValue(js, js_px);
|
|
||||||
|
|
||||||
if (!raw || !w || !h || !fmt)
|
|
||||||
return JS_ThrowReferenceError(js, "Invalid source.");
|
|
||||||
|
|
||||||
void *newraw = malloc(len);
|
|
||||||
memcpy(newraw, raw, len);
|
|
||||||
|
|
||||||
SDL_Surface *s = SDL_CreateSurfaceFrom(w, h, fmt, newraw, pitch);
|
|
||||||
|
|
||||||
if (!s)
|
|
||||||
return JS_ThrowInternalError(js, "Unable to create surface: %s", SDL_GetError());
|
|
||||||
|
|
||||||
return SDL_Surface2js(js, s);
|
|
||||||
)
|
|
||||||
|
|
||||||
// input: (gif image data)
|
// input: (gif image data)
|
||||||
JSC_CCALL(os_make_gif,
|
JSC_CCALL(os_make_gif,
|
||||||
@@ -1296,8 +1091,14 @@ JSC_CCALL(os_make_gif,
|
|||||||
ret = gif;
|
ret = gif;
|
||||||
|
|
||||||
if (frames == 1) {
|
if (frames == 1) {
|
||||||
// still image, so return just that
|
// still image, so return surface data object
|
||||||
JS_SetPropertyStr(js, gif, "surface", SDL_Surface2js(js,SDL_CreateSurfaceFrom(width,height,SDL_PIXELFORMAT_RGBA32, pixels, width*4)));
|
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));
|
||||||
|
JS_SetPropertyStr(js, surfData, "pixels", JS_NewArrayBufferCopy(js, pixels, width*height*4));
|
||||||
|
JS_SetPropertyStr(js, gif, "surface", surfData);
|
||||||
return gif;
|
return gif;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1306,19 +1107,18 @@ JSC_CCALL(os_make_gif,
|
|||||||
for (int i = 0; i < frames; i++) {
|
for (int i = 0; i < frames; i++) {
|
||||||
JSValue frame = JS_NewObject(js);
|
JSValue frame = JS_NewObject(js);
|
||||||
JS_SetPropertyStr(js, frame, "time", number2js(js,(float)delays[i]/1000.0));
|
JS_SetPropertyStr(js, frame, "time", number2js(js,(float)delays[i]/1000.0));
|
||||||
void *frame_pixels = malloc(width*height*4);
|
|
||||||
if (!frame_pixels) {
|
// Create surface data object instead of SDL_Surface
|
||||||
JS_FreeValue(js,gif);
|
JSValue surfData = JS_NewObject(js);
|
||||||
ret = JS_ThrowOutOfMemory(js);
|
JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, width));
|
||||||
goto CLEANUP;
|
JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, height));
|
||||||
}
|
JS_SetPropertyStr(js, surfData, "format", JS_NewString(js, "rgba32"));
|
||||||
memcpy(frame_pixels, (unsigned char*)pixels+(width*height*4*i), width*height*4);
|
JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, width*4));
|
||||||
SDL_Surface *framesurf = SDL_CreateSurfaceFrom(width,height,SDL_PIXELFORMAT_RGBA32,frame_pixels, width*4);
|
|
||||||
if (!framesurf) {
|
void *frame_pixels = (unsigned char*)pixels+(width*height*4*i);
|
||||||
ret = JS_ThrowReferenceError(js, "failed to create SDL_Surface: %s", SDL_GetError());
|
JS_SetPropertyStr(js, surfData, "pixels", JS_NewArrayBufferCopy(js, frame_pixels, width*height*4));
|
||||||
goto CLEANUP;
|
|
||||||
}
|
JS_SetPropertyStr(js, frame, "surface", surfData);
|
||||||
JS_SetPropertyStr(js, frame, "surface", SDL_Surface2js(js,framesurf));
|
|
||||||
JS_SetPropertyUint32(js, delay_arr, i, frame);
|
JS_SetPropertyUint32(js, delay_arr, i, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1332,10 +1132,16 @@ CLEANUP:
|
|||||||
JSValue aseframe2js(JSContext *js, ase_frame_t aframe)
|
JSValue aseframe2js(JSContext *js, ase_frame_t aframe)
|
||||||
{
|
{
|
||||||
JSValue frame = JS_NewObject(js);
|
JSValue frame = JS_NewObject(js);
|
||||||
void *frame_pixels = malloc(aframe.ase->w*aframe.ase->h*4);
|
|
||||||
memcpy(frame_pixels, aframe.pixels, aframe.ase->w*aframe.ase->h*4);
|
// Create surface data object instead of SDL_Surface
|
||||||
SDL_Surface *surf = SDL_CreateSurfaceFrom(aframe.ase->w, aframe.ase->h, SDL_PIXELFORMAT_RGBA32, frame_pixels, aframe.ase->w*4);
|
JSValue surfData = JS_NewObject(js);
|
||||||
JS_SetPropertyStr(js, frame, "surface", SDL_Surface2js(js,surf));
|
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_NewArrayBufferCopy(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));
|
JS_SetPropertyStr(js, frame, "time", number2js(js,(float)aframe.duration_milliseconds/1000.0));
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
@@ -1393,11 +1199,6 @@ JSC_CCALL(os_make_aseprite,
|
|||||||
cute_aseprite_free(ase);
|
cute_aseprite_free(ase);
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(os_make_surface,
|
|
||||||
HMM_Vec2 wh = js2vec2(js,argv[0]);
|
|
||||||
SDL_Surface *surface = SDL_CreateSurface(wh.x, wh.y, SDL_PIXELFORMAT_RGBA32);
|
|
||||||
ret = SDL_Surface2js(js, surface);
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: Implement this correctly
|
// TODO: Implement this correctly
|
||||||
JSC_CCALL(os_make_cursor,
|
JSC_CCALL(os_make_cursor,
|
||||||
@@ -1418,7 +1219,31 @@ JSC_CCALL(os_make_font,
|
|||||||
font *f = MakeFont(data, len, js2number(js,argv[1]));
|
font *f = MakeFont(data, len, js2number(js,argv[1]));
|
||||||
if (!f) return JS_ThrowReferenceError(js, "could not create font");
|
if (!f) return JS_ThrowReferenceError(js, "could not create font");
|
||||||
ret = font2js(js,f);
|
ret = font2js(js,f);
|
||||||
JS_SetPropertyStr(js, ret, "surface", SDL_Surface2js(js,f->surface));
|
|
||||||
|
// 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_NewArrayBufferCopy(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)); )
|
JSC_SCALL(os_system, ret = number2js(js,system(str)); )
|
||||||
@@ -1478,9 +1303,17 @@ static void render_frame(plm_t *mpeg, plm_frame_t *frame, datastream *ds) {
|
|||||||
uint8_t *rgb = malloc(frame->height*frame->width*4);
|
uint8_t *rgb = malloc(frame->height*frame->width*4);
|
||||||
memset(rgb,255,frame->height*frame->width*4);
|
memset(rgb,255,frame->height*frame->width*4);
|
||||||
plm_frame_to_rgba(frame, rgb, frame->width*4);
|
plm_frame_to_rgba(frame, rgb, frame->width*4);
|
||||||
SDL_Surface *surf = SDL_CreateSurfaceFrom(frame->width,frame->height, SDL_PIXELFORMAT_RGBA32, rgb, frame->width*4);
|
|
||||||
|
// Create surface data object instead of SDL_Surface
|
||||||
|
JSValue surfData = JS_NewObject(ds->js);
|
||||||
|
JS_SetPropertyStr(ds->js, surfData, "width", JS_NewInt32(ds->js, frame->width));
|
||||||
|
JS_SetPropertyStr(ds->js, surfData, "height", JS_NewInt32(ds->js, frame->height));
|
||||||
|
JS_SetPropertyStr(ds->js, surfData, "format", JS_NewString(ds->js, "rgba32"));
|
||||||
|
JS_SetPropertyStr(ds->js, surfData, "pitch", JS_NewInt32(ds->js, frame->width*4));
|
||||||
|
JS_SetPropertyStr(ds->js, surfData, "pixels", JS_NewArrayBufferCopy(ds->js, rgb, frame->height*frame->width*4));
|
||||||
|
|
||||||
JSValue s[1];
|
JSValue s[1];
|
||||||
s[0] = SDL_Surface2js(ds->js,surf);
|
s[0] = surfData;
|
||||||
JSValue cb = JS_DupValue(ds->js,ds->callback);
|
JSValue cb = JS_DupValue(ds->js,ds->callback);
|
||||||
JSValue ret = JS_Call(ds->js, cb, JS_UNDEFINED, 1, s);
|
JSValue ret = JS_Call(ds->js, cb, JS_UNDEFINED, 1, s);
|
||||||
JS_FreeValue(ds->js,cb);
|
JS_FreeValue(ds->js,cb);
|
||||||
@@ -1668,14 +1501,12 @@ static const JSCFunctionListEntry js_graphics_funcs[] = {
|
|||||||
MIST_FUNC_DEF(os, make_gif, 1),
|
MIST_FUNC_DEF(os, make_gif, 1),
|
||||||
MIST_FUNC_DEF(os, make_aseprite, 1),
|
MIST_FUNC_DEF(os, make_aseprite, 1),
|
||||||
MIST_FUNC_DEF(os, cull_sprites, 2),
|
MIST_FUNC_DEF(os, cull_sprites, 2),
|
||||||
MIST_FUNC_DEF(os, make_surface, 1),
|
|
||||||
MIST_FUNC_DEF(os, make_cursor, 1),
|
MIST_FUNC_DEF(os, make_cursor, 1),
|
||||||
MIST_FUNC_DEF(os, make_font, 2),
|
MIST_FUNC_DEF(os, make_font, 2),
|
||||||
MIST_FUNC_DEF(os, make_line_prim, 5),
|
MIST_FUNC_DEF(os, make_line_prim, 5),
|
||||||
MIST_FUNC_DEF(graphics, hsl_to_rgb, 3),
|
MIST_FUNC_DEF(graphics, hsl_to_rgb, 3),
|
||||||
MIST_FUNC_DEF(graphics, save_png, 4),
|
MIST_FUNC_DEF(graphics, save_png, 4),
|
||||||
MIST_FUNC_DEF(graphics, save_jpg, 4),
|
MIST_FUNC_DEF(graphics, save_jpg, 4),
|
||||||
MIST_FUNC_DEF(graphics, surface_from_pixels, 1),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const JSCFunctionListEntry js_video_funcs[] = {
|
static const JSCFunctionListEntry js_video_funcs[] = {
|
||||||
@@ -1763,36 +1594,41 @@ void ffi_load(JSContext *js)
|
|||||||
prosperon_rt *rt = JS_GetContextOpaque(js);
|
prosperon_rt *rt = JS_GetContextOpaque(js);
|
||||||
|
|
||||||
m_seedRand(&rt->mrand, time(NULL));
|
m_seedRand(&rt->mrand, time(NULL));
|
||||||
|
|
||||||
arrput(rt->module_registry, ((ModuleEntry){"io", js_io_use}));
|
// cell modules
|
||||||
arrput(rt->module_registry, ((ModuleEntry){"os", js_os_use}));
|
|
||||||
// actor module moved to hidden_fn
|
|
||||||
arrput(rt->module_registry, ((ModuleEntry){"input", js_input_use}));
|
|
||||||
arrput(rt->module_registry, MISTLINE(time));
|
arrput(rt->module_registry, MISTLINE(time));
|
||||||
arrput(rt->module_registry, ((ModuleEntry){"math", js_math_use}));
|
arrput(rt->module_registry, ((ModuleEntry){"math", js_math_use}));
|
||||||
|
arrput(rt->module_registry, MISTLINE(blob));
|
||||||
|
|
||||||
|
// extra
|
||||||
|
arrput(rt->module_registry, ((ModuleEntry){"io", js_io_use}));
|
||||||
|
arrput(rt->module_registry, ((ModuleEntry){"os", js_os_use}));
|
||||||
|
arrput(rt->module_registry, ((ModuleEntry){"input", js_input_use}));
|
||||||
|
arrput(rt->module_registry, MISTLINE(qr));
|
||||||
|
arrput(rt->module_registry, MISTLINE(http));
|
||||||
|
arrput(rt->module_registry, MISTLINE(crypto));
|
||||||
|
arrput(rt->module_registry, MISTLINE(miniz));
|
||||||
|
|
||||||
|
// power user
|
||||||
|
arrput(rt->module_registry, MISTLINE(js));
|
||||||
|
arrput(rt->module_registry, MISTLINE(debug));
|
||||||
|
arrput(rt->module_registry, MISTLINE(dmon));
|
||||||
|
arrput(rt->module_registry, MISTLINE(util));
|
||||||
|
|
||||||
|
// prosperon
|
||||||
|
arrput(rt->module_registry, ((ModuleEntry){"sdl_audio", js_sdl_audio_use}));
|
||||||
|
arrput(rt->module_registry, ((ModuleEntry){"sdl_video", js_sdl_video_use}));
|
||||||
|
arrput(rt->module_registry, ((ModuleEntry){"surface", js_sdl_surface_use}));
|
||||||
arrput(rt->module_registry, MISTLINE(spline));
|
arrput(rt->module_registry, MISTLINE(spline));
|
||||||
arrput(rt->module_registry, ((ModuleEntry){"geometry", js_geometry_use}));
|
arrput(rt->module_registry, ((ModuleEntry){"geometry", js_geometry_use}));
|
||||||
arrput(rt->module_registry, MISTLINE(graphics));
|
arrput(rt->module_registry, MISTLINE(graphics));
|
||||||
arrput(rt->module_registry, MISTLINE(js));
|
|
||||||
arrput(rt->module_registry, MISTLINE(util));
|
|
||||||
arrput(rt->module_registry, MISTLINE(video));
|
arrput(rt->module_registry, MISTLINE(video));
|
||||||
|
|
||||||
arrput(rt->module_registry, MISTLINE(soloud));
|
arrput(rt->module_registry, MISTLINE(soloud));
|
||||||
arrput(rt->module_registry, MISTLINE(layout));
|
arrput(rt->module_registry, MISTLINE(layout));
|
||||||
arrput(rt->module_registry, MISTLINE(miniz));
|
|
||||||
// arrput(rt->module_registry, MISTLINE(imgui));
|
// arrput(rt->module_registry, MISTLINE(imgui));
|
||||||
arrput(rt->module_registry, ((ModuleEntry){"camera", js_camera_use}));
|
arrput(rt->module_registry, ((ModuleEntry){"camera", js_camera_use}));
|
||||||
arrput(rt->module_registry, MISTLINE(debug));
|
|
||||||
arrput(rt->module_registry, MISTLINE(dmon));
|
|
||||||
arrput(rt->module_registry, MISTLINE(nota));
|
|
||||||
arrput(rt->module_registry, MISTLINE(enet));
|
|
||||||
arrput(rt->module_registry, MISTLINE(qr));
|
|
||||||
arrput(rt->module_registry, MISTLINE(crypto));
|
|
||||||
arrput(rt->module_registry, MISTLINE(blob));
|
|
||||||
arrput(rt->module_registry, MISTLINE(http));
|
|
||||||
arrput(rt->module_registry, ((ModuleEntry){"sdl_audio", js_sdl_audio_use}));
|
|
||||||
arrput(rt->module_registry, ((ModuleEntry){"sdl_video", js_sdl_video_use}));
|
|
||||||
// console module moved to hidden_fn
|
|
||||||
arrput(rt->module_registry, MISTLINE(rtree));
|
arrput(rt->module_registry, MISTLINE(rtree));
|
||||||
arrput(rt->module_registry, MISTLINE(sprite));
|
arrput(rt->module_registry, MISTLINE(sprite));
|
||||||
arrput(rt->module_registry, MISTLINE(transform));
|
arrput(rt->module_registry, MISTLINE(transform));
|
||||||
@@ -1812,7 +1648,6 @@ void ffi_load(JSContext *js)
|
|||||||
JSValue c_types = JS_NewObject(js);
|
JSValue c_types = JS_NewObject(js);
|
||||||
JS_SetPropertyStr(js,prosp, "c_types", c_types);
|
JS_SetPropertyStr(js,prosp, "c_types", c_types);
|
||||||
|
|
||||||
QJSCLASSPREP_FUNCS(SDL_Surface)
|
|
||||||
QJSCLASSPREP_FUNCS(font);
|
QJSCLASSPREP_FUNCS(font);
|
||||||
QJSCLASSPREP_FUNCS(datastream);
|
QJSCLASSPREP_FUNCS(datastream);
|
||||||
|
|
||||||
@@ -1849,6 +1684,8 @@ void ffi_load(JSContext *js)
|
|||||||
JS_SetPropertyStr(js, hidden_fn, "actor", js_actor_use(js));
|
JS_SetPropertyStr(js, hidden_fn, "actor", js_actor_use(js));
|
||||||
JS_SetPropertyStr(js, hidden_fn, "wota", js_wota_use(js));
|
JS_SetPropertyStr(js, hidden_fn, "wota", js_wota_use(js));
|
||||||
JS_SetPropertyStr(js, hidden_fn, "console", js_console_use(js));
|
JS_SetPropertyStr(js, hidden_fn, "console", js_console_use(js));
|
||||||
|
JS_SetPropertyStr(js, hidden_fn, "nota", js_nota_use(js));
|
||||||
|
JS_SetPropertyStr(js, hidden_fn, "enet", js_enet_use(js));
|
||||||
|
|
||||||
// Add functions that should only be accessible to engine.js
|
// Add functions that should only be accessible to engine.js
|
||||||
JS_SetPropertyStr(js, hidden_fn, "use_dyn", JS_NewCFunction(js, js_os_use_dyn, "use_dyn", 1));
|
JS_SetPropertyStr(js, hidden_fn, "use_dyn", JS_NewCFunction(js, js_os_use_dyn, "use_dyn", 1));
|
||||||
|
|||||||
364
source/qjs_sdl_surface.c
Normal file
364
source/qjs_sdl_surface.c
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
#include "qjs_sdl_surface.h"
|
||||||
|
#include "qjs_macros.h"
|
||||||
|
#include "jsffi.h"
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_gpu.h>
|
||||||
|
#include <SDL3/SDL_surface.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Helper functions from jsffi.c that need to be declared
|
||||||
|
extern JSValue number2js(JSContext *js, double g);
|
||||||
|
extern double js2number(JSContext *js, JSValue v);
|
||||||
|
extern rect js2rect(JSContext *js, JSValue v);
|
||||||
|
extern irect js2irect(JSContext *js, JSValue v);
|
||||||
|
extern colorf js2color(JSContext *js, JSValue v);
|
||||||
|
extern HMM_Vec2 js2vec2(JSContext *js, JSValue v);
|
||||||
|
|
||||||
|
// Type conversion functions
|
||||||
|
typedef struct { const char *name; SDL_PixelFormat fmt; } fmt_entry;
|
||||||
|
|
||||||
|
static const fmt_entry k_fmt_table[] = {
|
||||||
|
{ "rgba32", SDL_PIXELFORMAT_RGBA32 },
|
||||||
|
{ "argb32", SDL_PIXELFORMAT_ARGB32 },
|
||||||
|
{ "bgra32", SDL_PIXELFORMAT_BGRA32 },
|
||||||
|
{ "abgr32", SDL_PIXELFORMAT_ABGR32 },
|
||||||
|
{ "rgb565", SDL_PIXELFORMAT_RGB565 },
|
||||||
|
{ "bgr565", SDL_PIXELFORMAT_BGR565 },
|
||||||
|
{ "rgb24", SDL_PIXELFORMAT_RGB24 },
|
||||||
|
{ "bgr24", SDL_PIXELFORMAT_BGR24 },
|
||||||
|
{ "rgb332", SDL_PIXELFORMAT_RGB332 },
|
||||||
|
{ "rgba64", SDL_PIXELFORMAT_RGBA64 },
|
||||||
|
{ "rgb48", SDL_PIXELFORMAT_RGB48 },
|
||||||
|
{ NULL, SDL_PIXELFORMAT_UNKNOWN }
|
||||||
|
};
|
||||||
|
|
||||||
|
static JSValue pixelformat2js(JSContext *js, SDL_PixelFormat fmt)
|
||||||
|
{
|
||||||
|
fmt_entry *it;
|
||||||
|
for (it = (fmt_entry*)k_fmt_table; it->name; it++)
|
||||||
|
if (it->fmt == fmt)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (it->name)
|
||||||
|
return JS_NewString(js, it->name);
|
||||||
|
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDL_PixelFormat js2pixelformat(JSContext *js, JSValue v)
|
||||||
|
{
|
||||||
|
if (JS_IsUndefined(v)) return SDL_PIXELFORMAT_UNKNOWN;
|
||||||
|
const char *s = JS_ToCString(js, v);
|
||||||
|
if (!s) return SDL_PIXELFORMAT_UNKNOWN;
|
||||||
|
|
||||||
|
fmt_entry *it;
|
||||||
|
for (it = (fmt_entry*)k_fmt_table; it->name; it++)
|
||||||
|
if (!strcmp(it->name, s))
|
||||||
|
break;
|
||||||
|
|
||||||
|
JS_FreeCString(js,s);
|
||||||
|
return it->fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct { const char *name; SDL_ScaleMode mode; } scale_entry;
|
||||||
|
|
||||||
|
static const scale_entry k_scale_table[] = {
|
||||||
|
{ "nearest", SDL_SCALEMODE_NEAREST },
|
||||||
|
{ "linear", SDL_SCALEMODE_LINEAR },
|
||||||
|
{ NULL, SDL_SCALEMODE_LINEAR } /* fallback */
|
||||||
|
};
|
||||||
|
|
||||||
|
static JSValue scalemode2js(JSContext *js, SDL_ScaleMode mode){
|
||||||
|
const scale_entry *it;
|
||||||
|
for(it = k_scale_table; it->name; ++it)
|
||||||
|
if(it->mode == mode) break;
|
||||||
|
return JS_NewString(js, it->name ? it->name : "nearest");
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_ScaleMode js2SDL_ScaleMode(JSContext *js, JSValue v){
|
||||||
|
if(JS_IsUndefined(v)) return SDL_SCALEMODE_NEAREST;
|
||||||
|
const char *s = JS_ToCString(js, v);
|
||||||
|
if(!s) return SDL_SCALEMODE_NEAREST;
|
||||||
|
const scale_entry *it;
|
||||||
|
for(it = k_scale_table; it->name; ++it)
|
||||||
|
if(!strcmp(it->name, s)) break;
|
||||||
|
JS_FreeCString(js, s);
|
||||||
|
return it->mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SDL_Surface free function
|
||||||
|
void SDL_Surface_free(JSRuntime *rt, SDL_Surface *s) {
|
||||||
|
if (s->flags & SDL_SURFACE_PREALLOCATED)
|
||||||
|
free(s->pixels);
|
||||||
|
SDL_DestroySurface(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class definition
|
||||||
|
QJSCLASS(SDL_Surface,)
|
||||||
|
|
||||||
|
// SDL_Surface methods
|
||||||
|
JSC_CCALL(surface_blit,
|
||||||
|
SDL_Surface *dst = js2SDL_Surface(js, self);
|
||||||
|
|
||||||
|
irect dr = {0}, *pdr = NULL;
|
||||||
|
if(!JS_IsUndefined(argv[0])){ dr = js2irect(js, argv[0]); pdr = &dr; }
|
||||||
|
|
||||||
|
SDL_Surface *src = js2SDL_Surface(js, argv[1]);
|
||||||
|
if (!src)
|
||||||
|
return JS_ThrowReferenceError(js, "Argument must be a surface.");
|
||||||
|
|
||||||
|
irect sr = {0}, *psr = NULL;
|
||||||
|
if(!JS_IsUndefined(argv[2])){ sr = js2irect(js, argv[2]); psr = &sr; }
|
||||||
|
|
||||||
|
SDL_ScaleMode mode = js2SDL_ScaleMode(js, argv[3]);
|
||||||
|
|
||||||
|
SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_NONE);
|
||||||
|
|
||||||
|
SDL_BlitSurfaceScaled(src, psr, dst, pdr, mode);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_scale,
|
||||||
|
SDL_Surface *src = js2SDL_Surface(js,self);
|
||||||
|
HMM_Vec2 wh = js2vec2(js,argv[0]);
|
||||||
|
SDL_ScaleMode mode = js2SDL_ScaleMode(js, argv[1]);
|
||||||
|
SDL_Surface *new = SDL_ScaleSurface(src, wh.x, wh.y, mode);
|
||||||
|
ret = SDL_Surface2js(js,new);
|
||||||
|
)
|
||||||
|
|
||||||
|
static SDL_PixelFormatDetails pdetails = {
|
||||||
|
.format = SDL_PIXELFORMAT_RGBA8888, // Standard RGBA8888 format
|
||||||
|
.bits_per_pixel = 32, // 8 bits per channel, 4 channels = 32 bits
|
||||||
|
.bytes_per_pixel = 4, // 4 bytes per pixel
|
||||||
|
.padding = {0, 0}, // Unused padding
|
||||||
|
.Rmask = 0xFF000000, // Red mask
|
||||||
|
.Gmask = 0x00FF0000, // Green mask
|
||||||
|
.Bmask = 0x0000FF00, // Blue mask
|
||||||
|
.Amask = 0x000000FF, // Alpha mask
|
||||||
|
.Rbits = 8, // 8 bits for Red
|
||||||
|
.Gbits = 8, // 8 bits for Green
|
||||||
|
.Bbits = 8, // 8 bits for Blue
|
||||||
|
.Abits = 8, // 8 bits for Alpha
|
||||||
|
.Rshift = 24, // Red shift
|
||||||
|
.Gshift = 16, // Green shift
|
||||||
|
.Bshift = 8, // Blue shift
|
||||||
|
.Ashift = 0 // Alpha shift
|
||||||
|
};
|
||||||
|
|
||||||
|
JSC_CCALL(surface_fill,
|
||||||
|
SDL_Surface *src = js2SDL_Surface(js,self);
|
||||||
|
colorf color = js2color(js,argv[0]);
|
||||||
|
rect r = {
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.w = src->w,
|
||||||
|
.h = src->h
|
||||||
|
};
|
||||||
|
SDL_FillSurfaceRect(src, &r, SDL_MapRGBA(&pdetails, NULL, color.r*255,color.g*255,color.b*255,color.a*255));
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_rect,
|
||||||
|
SDL_Surface *dst = js2SDL_Surface(js,self);
|
||||||
|
rect r = js2rect(js,argv[0]);
|
||||||
|
colorf color = js2color(js,argv[1]);
|
||||||
|
SDL_FillSurfaceRect(dst,&r,SDL_MapRGBA(&pdetails,NULL, color.r*255,color.g*255,color.b*255,color.a*255));
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_convert,
|
||||||
|
SDL_Surface *surf = js2SDL_Surface(js,self);
|
||||||
|
SDL_PixelFormat fmt = js2pixelformat(js, argv[0]);
|
||||||
|
SDL_Surface *dst = SDL_ConvertSurface(surf, fmt);
|
||||||
|
if (!dst) return JS_ThrowInternalError(js, "Convert failed: %s", SDL_GetError());
|
||||||
|
|
||||||
|
return SDL_Surface2js(js, dst);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_dup,
|
||||||
|
SDL_Surface *surf = js2SDL_Surface(js,self);
|
||||||
|
SDL_Surface *conv = SDL_DuplicateSurface(surf);
|
||||||
|
if (!conv)
|
||||||
|
return JS_ThrowReferenceError(js, "could not blit to dup'd surface: %s", SDL_GetError());
|
||||||
|
|
||||||
|
return SDL_Surface2js(js,conv);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_pixels,
|
||||||
|
SDL_Surface *surf = js2SDL_Surface(js,self);
|
||||||
|
|
||||||
|
/* if (SDL_ISPIXELFORMAT_FOURCC(surf->format->format))
|
||||||
|
return JS_ThrowTypeError(js, "planar or FOURCC formats are not supported - convert first");
|
||||||
|
*/
|
||||||
|
|
||||||
|
int locked = 0;
|
||||||
|
if (SDL_MUSTLOCK(surf)) {
|
||||||
|
if (SDL_LockSurface(surf) < 0)
|
||||||
|
return JS_ThrowInternalError(js, "Lock surface failed: %s", SDL_GetError());
|
||||||
|
|
||||||
|
locked = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t byte_size = surf->pitch*surf->h;
|
||||||
|
|
||||||
|
ret = JS_NewArrayBufferCopy(js, surf->pixels, byte_size);
|
||||||
|
|
||||||
|
if (locked)
|
||||||
|
SDL_UnlockSurface(surf);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_get_width,
|
||||||
|
SDL_Surface *s = js2SDL_Surface(js,self);
|
||||||
|
return JS_NewFloat64(js, s->w);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_get_height,
|
||||||
|
SDL_Surface *s = js2SDL_Surface(js,self);
|
||||||
|
return JS_NewFloat64(js, s->h);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_get_format,
|
||||||
|
SDL_Surface *s = js2SDL_Surface(js,self);
|
||||||
|
return pixelformat2js(js, s->format);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_get_pitch,
|
||||||
|
SDL_Surface *s = js2SDL_Surface(js,self);
|
||||||
|
return JS_NewFloat64(js, s->pitch);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(surface_toJSON,
|
||||||
|
SDL_Surface *surf = js2SDL_Surface(js,self);
|
||||||
|
|
||||||
|
// Create the result object
|
||||||
|
JSValue obj = JS_NewObject(js);
|
||||||
|
|
||||||
|
// Add width and height
|
||||||
|
JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, surf->w));
|
||||||
|
JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, surf->h));
|
||||||
|
|
||||||
|
// Add format
|
||||||
|
JS_SetPropertyStr(js, obj, "format", pixelformat2js(js, surf->format));
|
||||||
|
|
||||||
|
// Add pitch
|
||||||
|
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_ThrowInternalError(js, "Lock surface failed: %s", SDL_GetError());
|
||||||
|
}
|
||||||
|
locked = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pixels as ArrayBuffer
|
||||||
|
size_t byte_size = surf->pitch * surf->h;
|
||||||
|
JSValue pixels = JS_NewArrayBufferCopy(js, surf->pixels, byte_size);
|
||||||
|
JS_SetPropertyStr(js, obj, "pixels", pixels);
|
||||||
|
|
||||||
|
// Unlock if we locked
|
||||||
|
if (locked)
|
||||||
|
SDL_UnlockSurface(surf);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
)
|
||||||
|
|
||||||
|
// Constructor for SDL_Surface
|
||||||
|
JSC_CCALL(surface_constructor,
|
||||||
|
if (argc < 1)
|
||||||
|
return JS_ThrowTypeError(js, "Surface constructor requires an object argument");
|
||||||
|
|
||||||
|
// Get width and height
|
||||||
|
int width, height;
|
||||||
|
JS_GETATOM(js, width, argv[0], width, number)
|
||||||
|
JS_GETATOM(js, height, argv[0], height, number)
|
||||||
|
|
||||||
|
if (!width || !height)
|
||||||
|
return JS_ThrowTypeError(js, "Surface constructor requires width and height properties");
|
||||||
|
|
||||||
|
// Check for pixel format
|
||||||
|
SDL_PixelFormat format = SDL_PIXELFORMAT_RGBA32; // default
|
||||||
|
JSValue format_val = JS_GetPropertyStr(js, argv[0], "format");
|
||||||
|
if (!JS_IsUndefined(format_val)) {
|
||||||
|
format = js2pixelformat(js, format_val);
|
||||||
|
}
|
||||||
|
JS_FreeValue(js, format_val);
|
||||||
|
|
||||||
|
// Check for pixel data
|
||||||
|
JSValue pixels_val = JS_GetPropertyStr(js, argv[0], "pixels");
|
||||||
|
if (!JS_IsUndefined(pixels_val)) {
|
||||||
|
// Create surface from pixel data
|
||||||
|
size_t len;
|
||||||
|
void *raw = JS_GetArrayBuffer(js, &len, pixels_val);
|
||||||
|
|
||||||
|
if (!raw) {
|
||||||
|
JS_FreeValue(js, pixels_val);
|
||||||
|
return JS_ThrowTypeError(js, "pixels property must be an ArrayBuffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get pitch if provided, otherwise calculate it
|
||||||
|
int pitch;
|
||||||
|
JSValue pitch_val = JS_GetPropertyStr(js, argv[0], "pitch");
|
||||||
|
if (!JS_IsUndefined(pitch_val)) {
|
||||||
|
pitch = js2number(js, pitch_val);
|
||||||
|
JS_FreeValue(js, pitch_val);
|
||||||
|
} else {
|
||||||
|
// Calculate pitch based on format
|
||||||
|
int bytes_per_pixel = SDL_BYTESPERPIXEL(format);
|
||||||
|
pitch = width * bytes_per_pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the pixel data
|
||||||
|
void *pixels_copy = malloc(len);
|
||||||
|
if (!pixels_copy) {
|
||||||
|
JS_FreeValue(js, pixels_val);
|
||||||
|
return JS_ThrowOutOfMemory(js);
|
||||||
|
}
|
||||||
|
memcpy(pixels_copy, raw, len);
|
||||||
|
|
||||||
|
SDL_Surface *surface = SDL_CreateSurfaceFrom(width, height, format, pixels_copy, pitch);
|
||||||
|
if (!surface) {
|
||||||
|
free(pixels_copy);
|
||||||
|
JS_FreeValue(js, pixels_val);
|
||||||
|
return JS_ThrowInternalError(js, "Failed to create surface from pixels: %s", SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_FreeValue(js, pixels_val);
|
||||||
|
ret = SDL_Surface2js(js, surface);
|
||||||
|
} else {
|
||||||
|
// Create blank surface
|
||||||
|
SDL_Surface *surface = SDL_CreateSurface(width, height, format);
|
||||||
|
if (!surface)
|
||||||
|
return JS_ThrowInternalError(js, "Failed to create surface: %s", SDL_GetError());
|
||||||
|
|
||||||
|
ret = SDL_Surface2js(js, surface);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
static const JSCFunctionListEntry js_SDL_Surface_funcs[] = {
|
||||||
|
MIST_FUNC_DEF(surface, blit, 4),
|
||||||
|
MIST_FUNC_DEF(surface, scale, 1),
|
||||||
|
MIST_FUNC_DEF(surface, fill,1),
|
||||||
|
MIST_FUNC_DEF(surface, rect,2),
|
||||||
|
MIST_FUNC_DEF(surface, dup, 0),
|
||||||
|
MIST_FUNC_DEF(surface, pixels, 0),
|
||||||
|
MIST_FUNC_DEF(surface, convert, 1),
|
||||||
|
MIST_FUNC_DEF(surface, toJSON, 0),
|
||||||
|
JS_CGETSET_DEF("width", js_surface_get_width, NULL),
|
||||||
|
JS_CGETSET_DEF("height", js_surface_get_height, NULL),
|
||||||
|
JS_CGETSET_DEF("format", js_surface_get_format, NULL),
|
||||||
|
JS_CGETSET_DEF("pitch", js_surface_get_pitch, NULL),
|
||||||
|
};
|
||||||
|
|
||||||
|
JSValue js_sdl_surface_use(JSContext *js)
|
||||||
|
{
|
||||||
|
QJSCLASSPREP_FUNCS(SDL_Surface)
|
||||||
|
|
||||||
|
// Return a constructor function that creates SDL_Surface objects
|
||||||
|
JSValue ctor = JS_NewCFunction2(js, js_surface_constructor, "surface", 1, JS_CFUNC_constructor, 0);
|
||||||
|
|
||||||
|
JSValue proto = JS_GetClassProto(js, js_SDL_Surface_id);
|
||||||
|
JS_SetConstructor(js, ctor, proto);
|
||||||
|
JS_FreeValue(js, proto);
|
||||||
|
|
||||||
|
return ctor;
|
||||||
|
}
|
||||||
16
source/qjs_sdl_surface.h
Normal file
16
source/qjs_sdl_surface.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef QJS_SDL_SURFACE_H
|
||||||
|
#define QJS_SDL_SURFACE_H
|
||||||
|
|
||||||
|
#include <quickjs.h>
|
||||||
|
#include <SDL3/SDL_surface.h>
|
||||||
|
|
||||||
|
JSValue js_sdl_surface_use(JSContext *js);
|
||||||
|
|
||||||
|
// Functions generated by QJSCLASS macro
|
||||||
|
JSValue SDL_Surface2js(JSContext *js, SDL_Surface *s);
|
||||||
|
SDL_Surface *js2SDL_Surface(JSContext *js, JSValue val);
|
||||||
|
|
||||||
|
// Free function for SDL_Surface
|
||||||
|
void SDL_Surface_free(JSRuntime *rt, SDL_Surface *s);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "qjs_sdl_video.h"
|
#include "qjs_sdl_video.h"
|
||||||
#include "jsffi.h"
|
#include "jsffi.h"
|
||||||
#include "qjs_macros.h"
|
#include "qjs_macros.h"
|
||||||
|
#include "qjs_sdl_surface.h"
|
||||||
#include "prosperon.h"
|
#include "prosperon.h"
|
||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
#include "transform.h"
|
#include "transform.h"
|
||||||
@@ -52,8 +53,6 @@ extern double js2number(JSContext *js, JSValue v);
|
|||||||
extern JSValue number2js(JSContext *js, double n);
|
extern JSValue number2js(JSContext *js, double n);
|
||||||
extern SDL_Texture *js2SDL_Texture(JSContext *js, JSValue v);
|
extern SDL_Texture *js2SDL_Texture(JSContext *js, JSValue v);
|
||||||
extern JSValue SDL_Texture2js(JSContext *js, SDL_Texture *t);
|
extern JSValue SDL_Texture2js(JSContext *js, SDL_Texture *t);
|
||||||
extern SDL_Surface *js2SDL_Surface(JSContext *js, JSValue v);
|
|
||||||
extern JSValue SDL_Surface2js(JSContext *js, SDL_Surface *s);
|
|
||||||
extern SDL_Window *js2SDL_Window(JSContext *js, JSValue v);
|
extern SDL_Window *js2SDL_Window(JSContext *js, JSValue v);
|
||||||
extern JSValue SDL_Window2js(JSContext *js, SDL_Window *w);
|
extern JSValue SDL_Window2js(JSContext *js, SDL_Window *w);
|
||||||
extern SDL_Renderer *js2SDL_Renderer(JSContext *js, JSValue v);
|
extern SDL_Renderer *js2SDL_Renderer(JSContext *js, JSValue v);
|
||||||
@@ -1802,8 +1801,6 @@ JSValue js_sdl_video_use(JSContext *js) {
|
|||||||
char id[64];
|
char id[64];
|
||||||
snprintf(id, sizeof(id), "video_%llu", (unsigned long long)SDL_GetTicks());
|
snprintf(id, sizeof(id), "video_%llu", (unsigned long long)SDL_GetTicks());
|
||||||
|
|
||||||
printf("id is %s\n", id);
|
|
||||||
|
|
||||||
// Prepare argv for create_actor
|
// Prepare argv for create_actor
|
||||||
// We need to create the actor on the main thread
|
// We need to create the actor on the main thread
|
||||||
const char *argv[] = {
|
const char *argv[] = {
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ var draw2d
|
|||||||
var graphics
|
var graphics
|
||||||
var os = use('os');
|
var os = use('os');
|
||||||
|
|
||||||
use('tracy').level = 1
|
|
||||||
|
|
||||||
// Create SDL video actor
|
// Create SDL video actor
|
||||||
var video = use('sdl_video');
|
var video = use('sdl_video');
|
||||||
var video_actor = {__ACTORDATA__:{id:video}};
|
var video_actor = {__ACTORDATA__:{id:video}};
|
||||||
@@ -61,9 +59,9 @@ function start_drawing() {
|
|||||||
var bunny_image = null;
|
var bunny_image = null;
|
||||||
try {
|
try {
|
||||||
bunny_image = graphics.texture('tests/bunny.png');
|
bunny_image = graphics.texture('tests/bunny.png');
|
||||||
console.log("Loaded bunny image");
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to load bunny image:", e);
|
console.log("Failed to load bunny image:", e);
|
||||||
|
console.log(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw_frame() {
|
function draw_frame() {
|
||||||
@@ -186,31 +184,28 @@ function start_drawing() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the bunny image if loaded
|
var img = "tests/bunny.png"
|
||||||
if (bunny_image) {
|
draw2d.image(img, {x: 500, y: 450, width: 64, height: 64});
|
||||||
// Static bunny
|
|
||||||
draw2d.image(bunny_image, {x: 500, y: 450, width: 64, height: 64});
|
// Rotating bunny
|
||||||
|
var rotation = t * 0.5;
|
||||||
// Rotating bunny
|
draw2d.image(
|
||||||
var rotation = t * 0.5;
|
img,
|
||||||
draw2d.image(
|
{x: 600, y: 450, width: 64, height: 64},
|
||||||
bunny_image,
|
rotation,
|
||||||
{x: 600, y: 450, width: 64, height: 64},
|
[0.5, 0.5] // Center anchor
|
||||||
rotation,
|
);
|
||||||
[0.5, 0.5] // Center anchor
|
|
||||||
);
|
// Bouncing bunny with tint
|
||||||
|
var bounce_y = 500 + Math.sin(t * 3) * 20;
|
||||||
// Bouncing bunny with tint
|
draw2d.image(
|
||||||
var bounce_y = 500 + Math.sin(t * 3) * 20;
|
img,
|
||||||
draw2d.image(
|
{x: 700, y: bounce_y, width: 48, height: 48},
|
||||||
bunny_image,
|
0,
|
||||||
{x: 700, y: bounce_y, width: 48, height: 48},
|
[0.5, 1], // Bottom center anchor
|
||||||
0,
|
[0, 0], // No shear
|
||||||
[0.5, 1], // Bottom center anchor
|
{color: [1, 0.5, 0.5, 1]} // Red tint
|
||||||
[0, 0], // No shear
|
);
|
||||||
{color: [1, 0.5, 0.5, 1]} // Red tint
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush all commands to renderer
|
// Flush all commands to renderer
|
||||||
draw2d.flush();
|
draw2d.flush();
|
||||||
|
|||||||
37
tests/surface.js
Normal file
37
tests/surface.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Test SDL_Surface module
|
||||||
|
var Surface = use('surface');
|
||||||
|
|
||||||
|
// Test creating a surface
|
||||||
|
var surf = new Surface({width: 100, height: 100});
|
||||||
|
console.log("Created surface:", surf.width, "x", surf.height);
|
||||||
|
|
||||||
|
console.log(json.encode(surf))
|
||||||
|
|
||||||
|
// Test fill
|
||||||
|
surf.fill([1, 0, 0, 1]); // Red
|
||||||
|
|
||||||
|
// Test dup
|
||||||
|
var surf2 = surf.dup();
|
||||||
|
console.log("Duplicated surface:", surf2.width, "x", surf2.height);
|
||||||
|
|
||||||
|
// Test scale
|
||||||
|
var surf3 = surf.scale([50, 50], "linear");
|
||||||
|
console.log("Scaled surface:", surf3.width, "x", surf3.height);
|
||||||
|
|
||||||
|
// Test format
|
||||||
|
console.log("Surface format:", surf.format);
|
||||||
|
|
||||||
|
// Test pixels
|
||||||
|
var pixels = surf.pixels();
|
||||||
|
console.log("Got pixels array buffer, length:", pixels.byteLength);
|
||||||
|
|
||||||
|
// Test creating surface with custom format
|
||||||
|
var surf4 = new Surface({width: 64, height: 64, format: "rgb24"});
|
||||||
|
console.log("Created RGB24 surface:", surf4.width, "x", surf4.height, "format:", surf4.format);
|
||||||
|
|
||||||
|
// Test creating surface from pixels
|
||||||
|
var pixelData = new ArrayBuffer(32 * 32 * 4); // 32x32 RGBA
|
||||||
|
var surf5 = new Surface({width: 32, height: 32, pixels: pixelData});
|
||||||
|
console.log("Created surface from pixels:", surf5.width, "x", surf5.height);
|
||||||
|
|
||||||
|
console.log("Surface module test passed!");
|
||||||
Reference in New Issue
Block a user