Compare commits

...

3 Commits

Author SHA1 Message Date
alonso.torres
da4920a079 WIPPP 2026-01-19 10:37:59 +01:00
alonso.torres
6fd8ef5574 WIP 2026-01-15 09:35:27 +01:00
alonso.torres
cb29100f93 WIP 2026-01-15 09:35:27 +01:00
12 changed files with 411 additions and 130 deletions

View File

@@ -58,7 +58,8 @@
:share-id share-id
:object-id (mapv :id objects)
:route "objects"
:skip-children skip-children}
:skip-children skip-children
:wasm "true"}
uri (-> (cf/get :public-uri)
(assoc :path "/render.html")
(assoc :query (u/map->query-string params)))]

View File

@@ -33,7 +33,9 @@
:page-id page-id
:share-id share-id
:object-id object-id
:route "objects"}]
:route "objects"
;;:wasm "true"
}]
(-> base-uri
(assoc :path "/render.html")
(assoc :query (u/map->query-string params)))))

View File

@@ -45,7 +45,9 @@
[app.main.ui.shapes.svg-raw :as svg-raw]
[app.main.ui.shapes.text :as text]
[app.main.ui.shapes.text.fontfaces :as ff]
[app.render-wasm.api :as wasm.api]
[app.util.dom :as dom]
[app.util.globals :as g]
[app.util.http :as http]
[app.util.strings :as ust]
[app.util.thumbnails :as th]
@@ -53,6 +55,7 @@
[beicon.v2.core :as rx]
[clojure.set :as set]
[cuerdas.core :as str]
[promesa.core :as p]
[rumext.v2 :as mf]))
(def ^:const viewbox-decimal-precision 3)
@@ -171,6 +174,8 @@
;; Don't wrap svg elements inside a <g> otherwise some can break
[:> svg-raw-wrapper {:shape shape :frame frame}]))))))
(set! wasm.api/shape-wrapper-factory shape-wrapper-factory)
(defn format-viewbox
"Format a viewbox given a rectangle"
[{:keys [x y width height] :or {x 0 y 0 width 100 height 100}}]
@@ -480,6 +485,48 @@
[:& ff/fontfaces-style {:fonts fonts}]
[:& shape-wrapper {:shape object}]]]]))
(mf/defc object-wasm
{::mf/wrap [mf/memo]}
[{:keys [objects object-id embed skip-children]
:or {embed false}
:as props}]
(let [object (get objects object-id)
object (cond-> object
(:hide-fill-on-export object)
(assoc :fills [])
skip-children
(assoc :shapes []))
{:keys [width height] :as bounds}
(gsb/get-object-bounds objects object {:ignore-margin? false})
vbox (format-viewbox bounds)
zoom 1
canvas-ref (mf/use-ref nil)]
(mf/use-effect
(fn []
(let [canvas (mf/ref-val canvas-ref)]
(->> @wasm.api/module
(p/fmap
(fn [ready?]
(when ready?
(try
(when (wasm.api/init-canvas-context canvas)
(wasm.api/initialize-viewport
objects zoom vbox "transparent"
(fn []
(wasm.api/render-sync-shape object-id)
(dom/set-attribute! canvas "id" (dm/str "screenshot-" object-id)))))
(catch :default e
(js/console.error "Error initializing canvas context:" e)
false)))))))))
[:canvas {:ref canvas-ref
:width width
:height height
:style {:background "red"}}]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SPRITES (DEBUG)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@@ -63,7 +63,7 @@
(mf/defc object-svg
{::mf/wrap-props false}
[{:keys [object-id embed skip-children]}]
[{:keys [object-id embed skip-children wasm]}]
(let [objects (mf/deref ref:objects)]
;; Set the globa CSS to assign the page size, needed for PDF
@@ -77,27 +77,42 @@
(mth/ceil height) "px")}))))
(when objects
[:& (mf/provider ctx/is-render?) {:value true}
[:& render/object-svg
{:objects objects
:object-id object-id
:embed embed
:skip-children skip-children}]])))
(if wasm
[:& render/object-wasm
{:objects objects
:object-id object-id
:embed embed
:skip-children skip-children}]
(mf/defc objects-svg
{::mf/wrap-props false}
[{:keys [object-ids embed skip-children]}]
(when-let [objects (mf/deref ref:objects)]
(for [object-id object-ids]
(let [objects (render/adapt-objects-for-shape objects object-id)]
[:& (mf/provider ctx/is-render?) {:value true}
[:& render/object-svg
{:objects objects
:key (str object-id)
:object-id object-id
:embed embed
:skip-children skip-children}]]))))
(mf/defc objects-svg
{::mf/wrap-props false}
[{:keys [object-ids embed skip-children wasm]}]
(when-let [objects (mf/deref ref:objects)]
(for [object-id object-ids]
(let [objects (render/adapt-objects-for-shape objects object-id)]
(if wasm
[:& render/object-wasm
{:objects objects
:key (str object-id)
:object-id object-id
:embed embed
:skip-children skip-children}]
[:& (mf/provider ctx/is-render?) {:value true}
[:& render/object-svg
{:objects objects
:key (str object-id)
:object-id object-id
:embed embed
:skip-children skip-children}]])))))
(defn- fetch-objects-bundle
[& {:keys [file-id page-id share-id object-id] :as options}]
(ptk/reify ::fetch-objects-bundle
@@ -136,7 +151,7 @@
(defn- render-objects
[params]
(try
(let [{:keys [file-id page-id embed share-id object-id skip-children] :as params}
(let [{:keys [file-id page-id embed share-id object-id skip-children wasm] :as params}
(coerce-render-objects-params params)]
(st/emit! (fetch-objects-bundle :file-id file-id :page-id page-id :share-id share-id :object-id object-id))
(if (uuid? object-id)
@@ -147,7 +162,8 @@
:share-id share-id
:object-id object-id
:embed embed
:skip-children skip-children}])
:skip-children skip-children
:wasm wasm}])
(mf/html
[:& objects-svg
@@ -156,7 +172,8 @@
:share-id share-id
:object-ids (into #{} object-id)
:embed embed
:skip-children skip-children}])))
:skip-children skip-children
:wasm wasm}])))
(catch :default cause
(when-let [explain (-> cause ex-data ::sm/explain)]
(js/console.log "Unexpected error")
@@ -307,6 +324,3 @@
(defn ^:dev/after-load after-load
[]
(reinit))

View File

@@ -23,7 +23,6 @@
[app.common.uuid :as uuid]
[app.config :as cf]
[app.main.refs :as refs]
[app.main.render :as render]
[app.main.store :as st]
[app.main.ui.shapes.text]
[app.main.worker :as mw]
@@ -74,6 +73,9 @@
(def noop-fn
(constantly nil))
;;
(def shape-wrapper-factory nil)
;; Based on app.main.render/object-svg
(mf/defc object-svg
{::mf/props :obj}
@@ -81,7 +83,7 @@
(let [objects (mf/deref refs/workspace-page-objects)
shape-wrapper
(mf/with-memo [shape]
(render/shape-wrapper-factory objects))]
(shape-wrapper-factory objects))]
[:svg {:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
@@ -915,84 +917,85 @@
(defn set-object
[shape]
(perf/begin-measure "set-object")
(let [shape (svg-filters/apply-svg-derived shape)
id (dm/get-prop shape :id)
type (dm/get-prop shape :type)
(when shape
(let [shape (svg-filters/apply-svg-derived shape)
id (dm/get-prop shape :id)
type (dm/get-prop shape :type)
parent-id (get shape :parent-id)
masked (get shape :masked-group)
selrect (get shape :selrect)
constraint-h (get shape :constraints-h)
constraint-v (get shape :constraints-v)
clip-content (if (= type :frame)
(not (get shape :show-content))
false)
rotation (get shape :rotation)
transform (get shape :transform)
parent-id (get shape :parent-id)
masked (get shape :masked-group)
selrect (get shape :selrect)
constraint-h (get shape :constraints-h)
constraint-v (get shape :constraints-v)
clip-content (if (= type :frame)
(not (get shape :show-content))
false)
rotation (get shape :rotation)
transform (get shape :transform)
fills (get shape :fills)
strokes (if (= type :group)
[] (get shape :strokes))
children (get shape :shapes)
blend-mode (get shape :blend-mode)
opacity (get shape :opacity)
hidden (get shape :hidden)
content (let [content (get shape :content)]
(if (= type :text)
(ensure-text-content content)
content))
bool-type (get shape :bool-type)
grow-type (get shape :grow-type)
blur (get shape :blur)
svg-attrs (get shape :svg-attrs)
shadows (get shape :shadow)
corners (map #(get shape %) [:r1 :r2 :r3 :r4])]
fills (get shape :fills)
strokes (if (= type :group)
[] (get shape :strokes))
children (get shape :shapes)
blend-mode (get shape :blend-mode)
opacity (get shape :opacity)
hidden (get shape :hidden)
content (let [content (get shape :content)]
(if (= type :text)
(ensure-text-content content)
content))
bool-type (get shape :bool-type)
grow-type (get shape :grow-type)
blur (get shape :blur)
svg-attrs (get shape :svg-attrs)
shadows (get shape :shadow)
corners (map #(get shape %) [:r1 :r2 :r3 :r4])]
(use-shape id)
(set-parent-id parent-id)
(set-shape-type type)
(set-shape-clip-content clip-content)
(set-shape-constraints constraint-h constraint-v)
(use-shape id)
(set-parent-id parent-id)
(set-shape-type type)
(set-shape-clip-content clip-content)
(set-shape-constraints constraint-h constraint-v)
(set-shape-rotation rotation)
(set-shape-transform transform)
(set-shape-blend-mode blend-mode)
(set-shape-opacity opacity)
(set-shape-hidden hidden)
(set-shape-children children)
(set-shape-corners corners)
(set-shape-blur blur)
(when (= type :group)
(set-masked (boolean masked)))
(when (= type :bool)
(set-shape-bool-type bool-type))
(when (and (some? content)
(or (= type :path)
(= type :bool)))
(set-shape-path-content content))
(when (some? svg-attrs)
(set-shape-svg-attrs svg-attrs))
(when (and (some? content) (= type :svg-raw))
(set-shape-svg-raw-content (get-static-markup shape)))
(set-shape-shadows shadows)
(when (= type :text)
(set-shape-grow-type grow-type))
(set-shape-rotation rotation)
(set-shape-transform transform)
(set-shape-blend-mode blend-mode)
(set-shape-opacity opacity)
(set-shape-hidden hidden)
(set-shape-children children)
(set-shape-corners corners)
(set-shape-blur blur)
(when (= type :group)
(set-masked (boolean masked)))
(when (= type :bool)
(set-shape-bool-type bool-type))
(when (and (some? content)
(or (= type :path)
(= type :bool)))
(set-shape-path-content content))
(when (some? svg-attrs)
(set-shape-svg-attrs svg-attrs))
(when (and (some? content) (= type :svg-raw))
(set-shape-svg-raw-content (get-static-markup shape)))
(set-shape-shadows shadows)
(when (= type :text)
(set-shape-grow-type grow-type))
(set-shape-layout shape)
(set-shape-selrect selrect)
(set-shape-layout shape)
(set-shape-selrect selrect)
(let [pending_thumbnails (into [] (concat
(set-shape-text-content id content)
(set-shape-text-images id content true)
(set-shape-fills id fills true)
(set-shape-strokes id strokes true)))
pending_full (into [] (concat
(set-shape-text-images id content false)
(set-shape-fills id fills false)
(set-shape-strokes id strokes false)))]
(perf/end-measure "set-object")
{:thumbnails pending_thumbnails
:full pending_full})))
(let [pending_thumbnails (into [] (concat
(set-shape-text-content id content)
(set-shape-text-images id content true)
(set-shape-fills id fills true)
(set-shape-strokes id strokes true)))
pending_full (into [] (concat
(set-shape-text-images id content false)
(set-shape-fills id fills false)
(set-shape-strokes id strokes false)))]
(perf/end-measure "set-object")
{:thumbnails pending_thumbnails
:full pending_full}))))
(defn update-text-layouts
[shapes]
@@ -1055,8 +1058,9 @@
(perf/end-measure "set-objects")
(process-pending shapes thumbnails full noop-fn
(fn []
(when render-callback (render-callback))
(render-finish)
(if render-callback
(render-callback)
(render-finish))
(ug/dispatch! (ug/event "penpot:wasm:set-objects")))))))
(defn clear-focus-mode
@@ -1448,6 +1452,35 @@
result)))
(defn render-shape-pixels
[shape-id]
(let [buffer (uuid/get-u32 shape-id)
offset
(h/call wasm/internal-module "_render_shape_pixels"
(aget buffer 0)
(aget buffer 1)
(aget buffer 2)
(aget buffer 3))
offset-32
(mem/->offset-32 offset)
heap (mem/get-heap-u8)
heapu32 (mem/get-heap-u32)
length (aget heapu32 (mem/->offset-32 offset))
width (aget heapu32 (+ (mem/->offset-32 offset) 1))
height (aget heapu32 (+ (mem/->offset-32 offset) 2))
result
(dr/read-image-bytes heap (+ offset 12) length)
]
(mem/free)
result
))
(defn init-wasm-module
[module]
(let [default-fn (unchecked-get module "default")

View File

@@ -45,6 +45,12 @@
:center (gpt/point cx cy)
:transform (gmt/matrix a b c d e f)}))
(defn read-image-bytes
[heap offset length]
(.slice ^js heap offset (+ offset length))
)
(defn read-position-data-entry
[heapu32 heapf32 offset]
(let [paragraph (aget heapu32 (+ offset 0))

View File

@@ -6,6 +6,8 @@
(ns debug
(:require
[app.render-wasm.wasm :as wasm]
[app.render-wasm.api :as wasm.api]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.repair :as cfr]
@@ -456,3 +458,46 @@
(defn ^:export network-averages
[]
(.log js/console (clj->js @http/network-averages)))
(defn ^:export export-image
[]
#_(let [texture (.createTexture ^js wasm/gl-context)]
(.bindTexture ^js wasm/gl-context (.-TEXTURE_2D ^js wasm/gl-context) texture)
(.texImage2D
^js wasm/gl-context
(.-TEXTURE_2D ^js wasm/gl-context)
0
(.-RGBA ^js wasm/gl-context)
800
600
0
(.-RGBA ^js wasm/gl-context)
(.-UNSIGNED_BYTE ^js wasm/gl-context)
nil)
;;(.log js/console )
(let [texture-id (wasm.api/get-texture-id-for-gl-object texture)
objects (dsh/lookup-page-objects @st/state)
shape-id (->> (get-selected @st/state) first)]
(wasm.api/render-to-texture shape-id texture-id))
)
(let [objects (dsh/lookup-page-objects @st/state)
shape-id (->> (get-selected @st/state) first)
bytes (wasm.api/render-shape-pixels shape-id)
_ (.log js/console (clj->js bytes))
blob (js/Blob. #js [bytes] #js {:type "image/png"})
url (.createObjectURL js/URL blob)
a (.createElement js/document "a")]
(set! (.-href a) url)
(set! (.-download a) "export.png")
(.click a)
(.revokeObjectURL js/URL url)
(.log js/console bytes)
))

View File

@@ -738,6 +738,28 @@ pub extern "C" fn end_temp_objects() {
}
}
#[no_mangle]
pub extern "C" fn render_shape_pixels(a: u32, b: u32, c: u32, d: u32) -> *mut u8 {
let id = uuid_from_u32_quartet(a, b, c, d);
with_state_mut!(state, {
let (data, width, height) = state.render_shape_pixels(&id, performance::get_time())
.expect("Cannot render into texture");
let len = data.len() as u32;
println!(">{len} {width} {height}");
let mut buf = Vec::with_capacity(4 + data.len());
buf.extend_from_slice(&len.to_le_bytes());
buf.extend_from_slice(&width.to_le_bytes());
buf.extend_from_slice(&height.to_le_bytes());
buf.extend_from_slice(&data);
println!("{len:?}");
mem::write_bytes(buf)
})
}
fn main() {
#[cfg(target_arch = "wasm32")]
init_gl!();

View File

@@ -17,6 +17,7 @@ use std::borrow::Cow;
use std::collections::HashSet;
use gpu_state::GpuState;
use options::RenderOptions;
pub use surfaces::{SurfaceId, Surfaces};
@@ -40,6 +41,7 @@ const NODE_BATCH_THRESHOLD: i32 = 3;
type ClipStack = Vec<(Rect, Option<Corners>, Matrix)>;
#[derive(Debug)]
pub struct NodeRenderState {
pub id: Uuid,
// We use this bool to keep that we've traversed all the children inside this node.
@@ -535,7 +537,7 @@ impl RenderState {
);
}
pub fn apply_drawing_to_render_canvas(&mut self, shape: Option<&Shape>) {
pub fn apply_drawing_to_render_canvas(&mut self, shape: Option<&Shape>, target: SurfaceId) {
performance::begin_measure!("apply_drawing_to_render_canvas");
let paint = skia::Paint::default();
@@ -543,12 +545,12 @@ impl RenderState {
// Only draw surfaces that have content (dirty flag optimization)
if self.surfaces.is_dirty(SurfaceId::TextDropShadows) {
self.surfaces
.draw_into(SurfaceId::TextDropShadows, SurfaceId::Current, Some(&paint));
.draw_into(SurfaceId::TextDropShadows, target, Some(&paint));
}
if self.surfaces.is_dirty(SurfaceId::Fills) {
self.surfaces
.draw_into(SurfaceId::Fills, SurfaceId::Current, Some(&paint));
.draw_into(SurfaceId::Fills, target, Some(&paint));
}
let mut render_overlay_below_strokes = false;
@@ -558,17 +560,17 @@ impl RenderState {
if render_overlay_below_strokes && self.surfaces.is_dirty(SurfaceId::InnerShadows) {
self.surfaces
.draw_into(SurfaceId::InnerShadows, SurfaceId::Current, Some(&paint));
.draw_into(SurfaceId::InnerShadows, target, Some(&paint));
}
if self.surfaces.is_dirty(SurfaceId::Strokes) {
self.surfaces
.draw_into(SurfaceId::Strokes, SurfaceId::Current, Some(&paint));
.draw_into(SurfaceId::Strokes, target, Some(&paint));
}
if !render_overlay_below_strokes && self.surfaces.is_dirty(SurfaceId::InnerShadows) {
self.surfaces
.draw_into(SurfaceId::InnerShadows, SurfaceId::Current, Some(&paint));
.draw_into(SurfaceId::InnerShadows, target, Some(&paint));
}
// Build mask of dirty surfaces that need clearing
@@ -641,6 +643,7 @@ impl RenderState {
offset: Option<(f32, f32)>,
parent_shadows: Option<Vec<skia_safe::Paint>>,
) {
println!(" >Shape:{:?}", shape.id);
let surface_ids = fills_surface_id as u32
| strokes_surface_id as u32
| innershadows_surface_id as u32
@@ -956,7 +959,7 @@ impl RenderState {
}
if apply_to_current_surface {
self.apply_drawing_to_render_canvas(Some(&shape));
self.apply_drawing_to_render_canvas(Some(&shape), SurfaceId::Current);
}
// Only restore if we saved (optimization for simple shapes)
@@ -1086,7 +1089,7 @@ impl RenderState {
self.current_tile = None;
self.render_in_progress = true;
self.apply_drawing_to_render_canvas(None);
self.apply_drawing_to_render_canvas(None, SurfaceId::Current);
if sync_render {
self.render_shape_tree_sync(base_object, tree, timestamp)?;
@@ -1141,6 +1144,48 @@ impl RenderState {
Ok(())
}
pub fn render_shape_pixels(
&mut self,
id: &Uuid,
tree: ShapesPoolRef,
timestamp: i32,
) -> Result<(Vec<u8>, i32, i32), String> {
// let width = 800;
// let height = 600;
if tree.len() != 0 {
// self.render_shape_tree_partial(Some(id), tree, timestamp, false)?;
let shape = tree.get(id).unwrap();
let scale = 1.0;
self.surfaces.update_render_context(shape.extrect(tree, scale), scale);
self.pending_nodes.push(NodeRenderState {
id: *id,
visited_children: false,
clip_bounds: None,
visited_mask: false,
mask: false,
});
self.render_shape_tree_partial_uncached(tree, timestamp, false, true)?;
} else {
println!("Empty tree");
}
self.surfaces.flush_and_submit(&mut self.gpu_state, SurfaceId::Export);
let image = self.surfaces.snapshot(SurfaceId::Export);
let data = image.encode(
&mut self.gpu_state.context,
skia::EncodedImageFormat::PNG,
100
).expect("PNG encode failed");
let skia::ISize { width, height } = image.dimensions();
Ok((data.as_bytes().to_vec(), width, height))
}
#[inline]
pub fn should_stop_rendering(&self, iteration: i32, timestamp: i32) -> bool {
iteration % NODE_BATCH_THRESHOLD == 0
@@ -1271,6 +1316,7 @@ impl RenderState {
element_strokes.to_mut().clear_fills();
element_strokes.to_mut().clear_shadows();
element_strokes.to_mut().clip_content = false;
println!(">in clip render");
self.render_shape(
&element_strokes,
clip_bounds,
@@ -1440,6 +1486,7 @@ impl RenderState {
}
state.with_nested_blurs_suppressed(|state| {
println!(">render drop black shadow");
state.render_shape(
&plain_shape,
clip_bounds,
@@ -1507,11 +1554,18 @@ impl RenderState {
tree: ShapesPoolRef,
timestamp: i32,
allow_stop: bool,
export: bool,
) -> Result<(bool, bool), String> {
let mut iteration = 0;
let mut is_empty = true;
let mut target_surface = SurfaceId::Current;
if export {
target_surface = SurfaceId::Export;
}
while let Some(node_render_state) = self.pending_nodes.pop() {
println!("Node: {node_render_state:?}");
let node_id = node_render_state.id;
let visited_children = node_render_state.visited_children;
let visited_mask = node_render_state.visited_mask;
@@ -1557,7 +1611,7 @@ impl RenderState {
let scale = self.get_scale();
let has_effects = transformed_element.has_effects_that_extend_bounds();
let is_visible = if is_container {
let is_visible = export || if is_container {
// Containers (frames/groups) must always use extrect to include nested content
let extrect = transformed_element.extrect(tree, scale);
extrect.intersects(self.render_area)
@@ -1581,6 +1635,7 @@ impl RenderState {
}
if !is_visible {
println!(">NOT VISIBLE");
continue;
}
}
@@ -1697,6 +1752,7 @@ impl RenderState {
new_shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
self.with_nested_blurs_suppressed(|state| {
println!(">render_shape in if focus");
state.render_shape(
shadow_shape,
clip_bounds,
@@ -1731,7 +1787,7 @@ impl RenderState {
if let Some(clips) = clip_bounds.as_ref() {
let antialias = element.should_use_antialias(scale);
self.surfaces.canvas(SurfaceId::Current).save();
self.surfaces.canvas(target_surface).save();
for (bounds, corners, transform) in clips.iter() {
let mut total_matrix = Matrix::new_identity();
total_matrix.pre_scale((scale, scale), None);
@@ -1739,18 +1795,18 @@ impl RenderState {
total_matrix.pre_concat(transform);
self.surfaces
.canvas(SurfaceId::Current)
.canvas(target_surface)
.concat(&total_matrix);
if let Some(corners) = corners {
let rrect = RRect::new_rect_radii(*bounds, corners);
self.surfaces.canvas(SurfaceId::Current).clip_rrect(
self.surfaces.canvas(target_surface).clip_rrect(
rrect,
skia::ClipOp::Intersect,
antialias,
);
} else {
self.surfaces.canvas(SurfaceId::Current).clip_rect(
self.surfaces.canvas(target_surface).clip_rect(
*bounds,
skia::ClipOp::Intersect,
antialias,
@@ -1758,23 +1814,24 @@ impl RenderState {
}
self.surfaces
.canvas(SurfaceId::Current)
.canvas(target_surface)
.concat(&total_matrix.invert().unwrap_or_default());
}
self.surfaces
.draw_into(SurfaceId::DropShadows, SurfaceId::Current, None);
.draw_into(SurfaceId::DropShadows, target_surface, None);
self.surfaces.canvas(SurfaceId::Current).restore();
self.surfaces.canvas(target_surface).restore();
} else {
self.surfaces
.draw_into(SurfaceId::DropShadows, SurfaceId::Current, None);
.draw_into(SurfaceId::DropShadows, target_surface, None);
}
self.surfaces
.canvas(SurfaceId::DropShadows)
.clear(skia::Color::TRANSPARENT);
println!(">>>> render_shape segundo focus");
self.render_shape(
element,
clip_bounds.clone(),
@@ -1791,7 +1848,8 @@ impl RenderState {
.canvas(SurfaceId::DropShadows)
.clear(skia::Color::TRANSPARENT);
} else if visited_children {
self.apply_drawing_to_render_canvas(Some(element));
println!(">>>>apply_drawing_to_render_canvas");
self.apply_drawing_to_render_canvas(Some(element), target_surface);
}
// Skip nested state updates for flattened containers
@@ -1818,6 +1876,7 @@ impl RenderState {
});
if element.is_recursive() {
println!("!adding children");
let children_clip_bounds =
node_render_state.get_children_clip_bounds(element, None);
@@ -1859,6 +1918,7 @@ impl RenderState {
// We try to avoid doing too many calls to get_time
if allow_stop && self.should_stop_rendering(iteration, timestamp) {
println!("STOP");
return Ok((is_empty, true));
}
iteration += 1;
@@ -1898,7 +1958,7 @@ impl RenderState {
} else {
performance::begin_measure!("render_shape_tree::uncached");
let (is_empty, early_return) =
self.render_shape_tree_partial_uncached(tree, timestamp, allow_stop)?;
self.render_shape_tree_partial_uncached(tree, timestamp, allow_stop, false)?;
if early_return {
return Ok(());

View File

@@ -104,4 +104,38 @@ impl GpuState {
)
.unwrap()
}
#[allow(dead_code)]
pub fn create_surface_from_texture(
&mut self,
width: i32,
height: i32,
texture_id: u32
) -> skia::Surface {
let texture_info = TextureInfo {
target: gl::TEXTURE_2D,
id: texture_id,
format: gl::RGBA8,
protected: skia::gpu::Protected::No,
};
let backend_texture = unsafe{
gpu::backend_textures::make_gl(
(width, height),
gpu::Mipmapped::No,
texture_info,
String::from("export_texture"))
};
gpu::surfaces::wrap_backend_texture(
&mut self.context,
&backend_texture,
gpu::SurfaceOrigin::BottomLeft,
None,
skia::ColorType::RGBA8888,
None,
None,
).unwrap()
}
}

View File

@@ -17,17 +17,18 @@ const TILE_SIZE_MULTIPLIER: i32 = 2;
#[repr(u32)]
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SurfaceId {
Target = 0b00_0000_0001,
Filter = 0b00_0000_0010,
Cache = 0b00_0000_0100,
Current = 0b00_0000_1000,
Fills = 0b00_0001_0000,
Strokes = 0b00_0010_0000,
DropShadows = 0b00_0100_0000,
InnerShadows = 0b00_1000_0000,
TextDropShadows = 0b01_0000_0000,
UI = 0b10_0000_0000,
Debug = 0b10_0000_0001,
Target = 0b000_0000_0001,
Filter = 0b000_0000_0010,
Cache = 0b000_0000_0100,
Current = 0b000_0000_1000,
Fills = 0b000_0001_0000,
Strokes = 0b000_0010_0000,
DropShadows = 0b000_0100_0000,
InnerShadows = 0b000_1000_0000,
TextDropShadows = 0b001_0000_0000,
UI = 0b010_0000_0000,
Debug = 0b010_0000_0001,
Export = 0b100_0000_0001,
}
pub struct Surfaces {
@@ -52,6 +53,8 @@ pub struct Surfaces {
// for drawing debug info.
debug: skia::Surface,
// for drawing tiles.
export: skia::Surface,
tiles: TileTextureCache,
sampling_options: skia::SamplingOptions,
margins: skia::ISize,
@@ -90,6 +93,7 @@ impl Surfaces {
let ui = gpu_state.create_surface_with_dimensions("ui".to_string(), width, height);
let debug = gpu_state.create_surface_with_dimensions("debug".to_string(), width, height);
let export = gpu_state.create_target_surface(width, height);
let tiles = TileTextureCache::new();
Surfaces {
@@ -104,6 +108,7 @@ impl Surfaces {
shape_strokes,
ui,
debug,
export,
tiles,
sampling_options,
margins,
@@ -243,6 +248,9 @@ impl Surfaces {
if ids & SurfaceId::Debug as u32 != 0 {
f(self.get_mut(SurfaceId::Debug));
}
if ids & SurfaceId::Export as u32 != 0 {
f(self.get_mut(SurfaceId::Export));
}
performance::begin_measure!("apply_mut::flags");
}
@@ -266,7 +274,9 @@ impl Surfaces {
let surface_ids = SurfaceId::Fills as u32
| SurfaceId::Strokes as u32
| SurfaceId::InnerShadows as u32
| SurfaceId::TextDropShadows as u32;
| SurfaceId::TextDropShadows as u32
// | SurfaceId::Export as u32
;
// Clear surfaces before updating transformations to remove residual content
self.apply_mut(surface_ids, |s| {
@@ -278,6 +288,7 @@ impl Surfaces {
self.mark_dirty(SurfaceId::Strokes);
self.mark_dirty(SurfaceId::InnerShadows);
self.mark_dirty(SurfaceId::TextDropShadows);
self.mark_dirty(SurfaceId::Export);
// Update transformations
self.apply_mut(surface_ids, |s| {
@@ -289,7 +300,7 @@ impl Surfaces {
}
#[inline]
fn get_mut(&mut self, id: SurfaceId) -> &mut skia::Surface {
pub fn get_mut(&mut self, id: SurfaceId) -> &mut skia::Surface {
match id {
SurfaceId::Target => &mut self.target,
SurfaceId::Filter => &mut self.filter,
@@ -302,6 +313,7 @@ impl Surfaces {
SurfaceId::Strokes => &mut self.shape_strokes,
SurfaceId::Debug => &mut self.debug,
SurfaceId::UI => &mut self.ui,
SurfaceId::Export => &mut self.export
}
}

View File

@@ -99,6 +99,11 @@ impl<'a> State<'a> {
Ok(())
}
pub fn render_shape_pixels(&mut self, id: &Uuid, timestamp: i32) -> Result<(Vec<u8>, i32, i32), String> {
self.render_state
.render_shape_pixels(id, &self.shapes, timestamp)
}
pub fn start_render_loop(&mut self, timestamp: i32) -> Result<(), String> {
self.render_state
.start_render_loop(None, &self.shapes, timestamp, false)?;