0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-28 15:41:25 -05:00

Draw shapes from memory

This commit is contained in:
Alejandro Alonso 2024-10-08 11:32:25 +02:00
parent fa9004d12c
commit 0027e77861
4 changed files with 126 additions and 94 deletions

View file

@ -145,6 +145,17 @@ pub extern "C" fn make_rect(left: f32, top: f32, right: f32, bottom: f32) -> Box
})
}
#[no_mangle]
pub extern "C" fn alloc_rects(len: usize) -> *mut Rect {
// create a new mutable buffer with capacity `len`
let mut buf : Vec<Rect> = Vec::with_capacity(len);
let ptr = buf.as_mut_ptr();
// take ownership of the memory block and ensure the its destructor is not
// called when the object goes out of scope at the end of the function
std::mem::forget(buf);
return ptr;
}
/// Draws a rect at the specified coordinates with the give ncolor
/// # Safety
#[no_mangle]
@ -180,6 +191,23 @@ pub unsafe extern "C" fn reset_canvas(state: *mut State) {
(*state).surface.canvas().reset_matrix();
}
#[no_mangle]
pub unsafe fn draw_shapes(state: *mut State, ptr: *mut Rect, len: usize, zoom: f32, dx: f32, dy: f32) {
let state = unsafe { state.as_mut() }.expect("got an invalid state pointer");
reset_canvas(state);
scale(state, zoom, zoom);
translate(state, dx, dy);
// create a `Vec<Rect>` from the pointer to the linear memory and length
let buf = Vec::<Rect>::from_raw_parts(ptr, len, len);
for rect in buf.iter() {
let r = skia::Rect::new(rect.left, rect.top, rect.right, rect.bottom);
let color = skia::Color::from_argb(255, 0, 0, 0);
render_rect(&mut state.surface, r, color);
}
flush(state);
std::mem::forget(buf);
}
fn main() {
init_gl();
}

View file

@ -130,7 +130,7 @@
hover-top-frame-id (mf/use-state nil)
frame-hover (mf/use-state nil)
active-frames (mf/use-state #{})
canvas-set? (mf/use-state false)
canvas-init? (mf/use-ref false)
;; REFS
[viewport-ref
@ -273,16 +273,20 @@
(when (render-v2/is-enabled?)
;; set up canvas and first render
(mf/with-effect
[canvas-ref vbox' @canvas-set? base-objects zoom]
[canvas-ref]
(let [canvas (mf/ref-val canvas-ref)]
(when (and (some? vbox') (not @canvas-set?))
(p/then (render-v2/init) (fn []
(render-v2/set-canvas canvas vbox' zoom base-objects)
(swap! canvas-set? true))))))
(when (some? canvas)
(p/then (render-v2/init)
(fn []
(render-v2/set-canvas canvas vbox' zoom base-objects)
(mf/set-ref-val! canvas-init? true))))))
;; redraw when vbox or shapes change
(mf/with-effect
[vbox base-objects canvas-set? zoom]
(when @canvas-set?
[vbox base-objects canvas-init? zoom]
(js/console.log "jibiri")
(when (mf/ref-val canvas-init?)
(js/console.log "jibiri ho")
(render-v2/draw-canvas vbox zoom base-objects))))
(hooks/setup-dom-events zoom disable-paste in-viewport? workspace-read-only? drawing-tool drawing-path?)

View file

@ -16,6 +16,8 @@
(defonce ^:dynamic internal-module #js {})
(defonce ^:dynamic gpu-state #js {})
(defonce ^:dynamic shapes-ptr nil)
(defonce ^:dynamic shapes-size nil)
(defn draw-canvas [vbox zoom objects]
(let [draw-rect (gobj/get ^js internal-module "_draw_rect")
@ -23,22 +25,9 @@
reset-canvas (gobj/get ^js internal-module "_reset_canvas")
scale (gobj/get ^js internal-module "_scale")
flush (gobj/get ^js internal-module "_flush")
make-color (gobj/get ^js internal-module "_make_color")
make-rect (gobj/get ^js internal-module "_make_rect")
supported-shapes (filter (fn [shape] (not= (:type shape) :frame)) (vals objects))]
draw-shapes (gobj/get ^js internal-module "_draw_shapes")]
(js/requestAnimationFrame (fn []
(reset-canvas gpu-state)
(scale gpu-state zoom zoom)
(translate gpu-state (- (:x vbox)) (- (:y vbox)))
(doseq [shape supported-shapes]
(let [sr (:selrect shape)
[r g b] (cc/hex->rgb (-> shape :fills first :fill-color))
alpha (-> shape :fills first :fill-opacity)
color (make-color r g b alpha)
rect (make-rect (:x1 sr) (:y1 sr) (:x2 sr) (:y2 sr))]
(draw-rect gpu-state rect color)))
(flush gpu-state)))))
(draw-shapes gpu-state shapes-ptr shapes-size zoom (- (:x vbox)) (- (:y vbox)))))))
(defn set-canvas
[canvas vbox zoom objects]
@ -52,18 +41,31 @@
_ (.makeContextCurrent gl handle)
;; Initialize Skia
state (._init ^js internal-module (.-width canvas) (.-height canvas))
draw_rect (gobj/get ^js internal-module "_draw_rect")
translate (gobj/get ^js internal-module "_translate")
scale (gobj/get ^js internal-module "_scale")
resize_surface (gobj/get ^js internal-module "_resize_surface")]
alloc-rects (gobj/get ^js internal-module "_alloc_rects")
heap (gobj/get ^js internal-module "HEAPF32")
supported-shapes (filter (fn [shape] (not= (:type shape) :frame)) (vals objects))
ptr (alloc-rects (count supported-shapes))]
(set! (.-width canvas) (.-clientWidth canvas))
(set! (.-height canvas) (.-clientHeight canvas))
(set! gpu-state state)
(set! shapes-ptr ptr)
(set! shapes-size (count supported-shapes))
(doseq [[shape index] (zipmap supported-shapes (range))]
(let [sr (:selrect shape)
[r g b] (cc/hex->rgb (-> shape :fills first :fill-color))
alpha (-> shape :fills first :fill-opacity)
;; color (make-color r g b alpha)
;; rect (make-rect (:x1 sr) (:y1 sr) (:x2 sr) (:y2 sr))
;; Each F32 are 4 bytes and each rect has 4 F32
mem (js/Float32Array. (.-buffer heap) (+ ptr (* (* 4 4) index)) (* 4 4))]
;; (js-debugger)
(.set mem (js/Float32Array. (clj->js [(:x1 sr) (:y1 sr) (:x2 sr) (:y2 sr)])))))
(draw-canvas vbox zoom objects)
#_(draw_rect state 100 100 500 500)
(println "set-canvas ok" (.-width canvas) (.-height canvas))))
(defn on-init

View file

@ -28,7 +28,7 @@ var readyPromise = new Promise((resolve, reject) => {
readyPromiseResolve = resolve;
readyPromiseReject = reject;
});
["_draw_rect","_flush","_init","_main","_make_color","_make_rect","_reset_canvas","_resize_surface","_scale","_translate","getExceptionMessage","incrementExceptionRefcount","decrementExceptionRefcount","_memory","___indirect_function_table","onRuntimeInitialized"].forEach((prop) => {
["_alloc_rects","_draw_rect","_draw_shapes","_flush","_init","_main","_make_color","_make_rect","_reset_canvas","_resize_surface","_scale","_translate","getExceptionMessage","incrementExceptionRefcount","decrementExceptionRefcount","_memory","___indirect_function_table","onRuntimeInitialized"].forEach((prop) => {
if (!Object.getOwnPropertyDescriptor(readyPromise, prop)) {
Object.defineProperty(readyPromise, prop, {
get: () => abort('You are getting ' + prop + ' on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js'),
@ -700,7 +700,7 @@ function createWasm() {
}
}
wasmBinaryFile ??= findWasmBinary();
if (!wasmBinaryFile) wasmBinaryFile = findWasmBinary();
// If instantiation fails, reject the module ready promise.
instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult).catch(readyPromiseReject);
@ -752,51 +752,45 @@ function isExportedByForceFilesystem(name) {
name === 'removeRunDependency';
}
/**
* Intercept access to a global symbol. This enables us to give informative
* warnings/errors when folks attempt to use symbols they did not include in
* their build, or no symbols that no longer exist.
*/
function hookGlobalSymbolAccess(sym, func) {
if (typeof globalThis != 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) {
function missingGlobal(sym, msg) {
if (typeof globalThis != 'undefined') {
Object.defineProperty(globalThis, sym, {
configurable: true,
get() {
func();
warnOnce(`\`${sym}\` is not longer defined by emscripten. ${msg}`);
return undefined;
}
});
}
}
function missingGlobal(sym, msg) {
hookGlobalSymbolAccess(sym, () => {
warnOnce(`\`${sym}\` is not longer defined by emscripten. ${msg}`);
});
}
missingGlobal('buffer', 'Please use HEAP8.buffer or wasmMemory.buffer');
missingGlobal('asm', 'Please use wasmExports instead');
function missingLibrarySymbol(sym) {
hookGlobalSymbolAccess(sym, () => {
// Can't `abort()` here because it would break code that does runtime
// checks. e.g. `if (typeof SDL === 'undefined')`.
var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`;
// DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in
// library.js, which means $name for a JS name with no prefix, or name
// for a JS name like _name.
var librarySymbol = sym;
if (!librarySymbol.startsWith('_')) {
librarySymbol = '$' + sym;
}
msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`;
if (isExportedByForceFilesystem(sym)) {
msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you';
}
warnOnce(msg);
});
if (typeof globalThis != 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) {
Object.defineProperty(globalThis, sym, {
configurable: true,
get() {
// Can't `abort()` here because it would break code that does runtime
// checks. e.g. `if (typeof SDL === 'undefined')`.
var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`;
// DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in
// library.js, which means $name for a JS name with no prefix, or name
// for a JS name like _name.
var librarySymbol = sym;
if (!librarySymbol.startsWith('_')) {
librarySymbol = '$' + sym;
}
msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`;
if (isExportedByForceFilesystem(sym)) {
msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you';
}
warnOnce(msg);
return undefined;
}
});
}
// Any symbol that is not included from the JS library is also (by definition)
// not exported on the Module object.
unexportedRuntimeSymbol(sym);
@ -1311,7 +1305,12 @@ function dbg(...args) {
var _emscripten_date_now = () => Date.now();
var _emscripten_get_now = () => performance.now();
var _emscripten_get_now;
// Modern environment where performance.now() is supported:
// N.B. a shorter form "_emscripten_get_now = performance.now;" is
// unfortunately not allowed even in current browsers (e.g. FF Nightly 75).
_emscripten_get_now = () => performance.now();
;
var GLctx;
@ -5642,8 +5641,6 @@ function dbg(...args) {
},
filesystems:null,
syncFSRequests:0,
readFiles:{
},
FSStream:class {
constructor() {
// TODO(https://github.com/emscripten-core/emscripten/issues/21414):
@ -6543,6 +6540,7 @@ function dbg(...args) {
stream.stream_ops.open(stream);
}
if (Module['logReadFiles'] && !(flags & 1)) {
if (!FS.readFiles) FS.readFiles = {};
if (!(path in FS.readFiles)) {
FS.readFiles[path] = 1;
}
@ -6963,7 +6961,7 @@ function dbg(...args) {
createDevice(parent, name, input, output) {
var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name);
var mode = FS_getMode(!!input, !!output);
FS.createDevice.major ??= 64;
if (!FS.createDevice.major) FS.createDevice.major = 64;
var dev = FS.makedev(FS.createDevice.major++, 0);
// Create a fake device that a set of stream ops to emulate
// the old behavior.
@ -8567,11 +8565,13 @@ var _init = Module['_init'] = createExportWrapper('init', 2);
var _resize_surface = Module['_resize_surface'] = createExportWrapper('resize_surface', 3);
var _make_color = Module['_make_color'] = createExportWrapper('make_color', 4);
var _make_rect = Module['_make_rect'] = createExportWrapper('make_rect', 4);
var _alloc_rects = Module['_alloc_rects'] = createExportWrapper('alloc_rects', 1);
var _draw_rect = Module['_draw_rect'] = createExportWrapper('draw_rect', 3);
var _flush = Module['_flush'] = createExportWrapper('flush', 1);
var _translate = Module['_translate'] = createExportWrapper('translate', 3);
var _scale = Module['_scale'] = createExportWrapper('scale', 3);
var _reset_canvas = Module['_reset_canvas'] = createExportWrapper('reset_canvas', 1);
var _draw_shapes = Module['_draw_shapes'] = createExportWrapper('draw_shapes', 6);
var _main = Module['_main'] = createExportWrapper('main', 2);
var _fflush = createExportWrapper('fflush', 1);
var _free = createExportWrapper('free', 1);
@ -8617,10 +8617,10 @@ var dynCall_iiiiij = Module['dynCall_iiiiij'] = createExportWrapper('dynCall_iii
var dynCall_iiiiijj = Module['dynCall_iiiiijj'] = createExportWrapper('dynCall_iiiiijj', 9);
var dynCall_iiiiiijj = Module['dynCall_iiiiiijj'] = createExportWrapper('dynCall_iiiiiijj', 10);
function invoke_iii(index,a1,a2) {
function invoke_vii(index,a1,a2) {
var sp = stackSave();
try {
return getWasmTableEntry(index)(a1,a2);
getWasmTableEntry(index)(a1,a2);
} catch(e) {
stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e;
@ -8639,17 +8639,6 @@ function invoke_ii(index,a1) {
}
}
function invoke_vii(index,a1,a2) {
var sp = stackSave();
try {
getWasmTableEntry(index)(a1,a2);
} catch(e) {
stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e;
_setThrew(1, 0);
}
}
function invoke_viii(index,a1,a2,a3) {
var sp = stackSave();
try {
@ -8672,10 +8661,21 @@ function invoke_vi(index,a1) {
}
}
function invoke_iiii(index,a1,a2,a3) {
function invoke_iii(index,a1,a2) {
var sp = stackSave();
try {
return getWasmTableEntry(index)(a1,a2,a3);
return getWasmTableEntry(index)(a1,a2);
} catch(e) {
stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e;
_setThrew(1, 0);
}
}
function invoke_viiii(index,a1,a2,a3,a4) {
var sp = stackSave();
try {
getWasmTableEntry(index)(a1,a2,a3,a4);
} catch(e) {
stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e;
@ -8705,6 +8705,17 @@ function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6) {
}
}
function invoke_iiii(index,a1,a2,a3) {
var sp = stackSave();
try {
return getWasmTableEntry(index)(a1,a2,a3);
} catch(e) {
stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e;
_setThrew(1, 0);
}
}
function invoke_iiiii(index,a1,a2,a3,a4) {
var sp = stackSave();
try {
@ -8738,17 +8749,6 @@ function invoke_iiff(index,a1,a2,a3) {
}
}
function invoke_viiii(index,a1,a2,a3,a4) {
var sp = stackSave();
try {
getWasmTableEntry(index)(a1,a2,a3,a4);
} catch(e) {
stackRestore(sp);
if (!(e instanceof EmscriptenEH)) throw e;
_setThrew(1, 0);
}
}
function invoke_iiiiii(index,a1,a2,a3,a4,a5) {
var sp = stackSave();
try {
@ -9052,14 +9052,12 @@ var missingLibrarySymbols = [
'setImmediateWrapped',
'clearImmediateWrapped',
'polyfillSetImmediate',
'registerPostMainLoop',
'registerPreMainLoop',
'getPromise',
'makePromise',
'idsToPromises',
'makePromiseCallback',
'Browser_asyncPrepareDataCounter',
'safeRequestAnimationFrame',
'setMainLoop',
'isLeapYear',
'ydayFromDate',
'arraySum',