mirror of
https://github.com/penpot/penpot.git
synced 2026-07-02 20:35:33 +00:00
WIP
This commit is contained in:
parent
37add2c6d4
commit
7cd1410703
@ -5,12 +5,12 @@ use crate::emscripten::init_gl;
|
||||
|
||||
use crate::mem;
|
||||
use crate::render::{gpu_state::GpuState, RenderState};
|
||||
use crate::state::{State, TextEditorState, UIState};
|
||||
use crate::state::{DesignState, TextEditorState, UIState};
|
||||
|
||||
static mut DESIGN_STATE: *mut State = std::ptr::null_mut();
|
||||
static mut DESIGN_STATE: *mut DesignState = std::ptr::null_mut();
|
||||
|
||||
/// Design State.
|
||||
pub(crate) fn get_design_state() -> &'static mut State {
|
||||
pub(crate) fn get_design_state() -> &'static mut DesignState {
|
||||
unsafe {
|
||||
debug_assert!(!DESIGN_STATE.is_null(), "Design State is null");
|
||||
&mut *DESIGN_STATE
|
||||
@ -116,7 +116,7 @@ fn render_init(width: i32, height: i32) {
|
||||
/// Initializes DesignState.
|
||||
fn design_init() {
|
||||
unsafe {
|
||||
let design_state = State::new();
|
||||
let design_state = DesignState::new();
|
||||
DESIGN_STATE = Box::into_raw(Box::new(design_state));
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ pub mod walk;
|
||||
|
||||
use skia_safe::{self as skia, Matrix, RRect, Rect};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashSet};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use options::RenderOptions;
|
||||
pub use surfaces::{SurfaceId, Surfaces};
|
||||
@ -41,9 +41,9 @@ use crate::globals::get_design_state;
|
||||
use crate::shapes::{
|
||||
Blur, BlurType, Fill, Frame, Layout, Shadow, Shape, Stroke, StrokeKind, TextContent, Type, all_with_ancestors,
|
||||
};
|
||||
use crate::state::{RulerState, ShapesPoolMutRef, ShapesPoolRef};
|
||||
use crate::tiles::{self, PendingTiles, TileRect};
|
||||
use crate::uuid::Uuid;
|
||||
use crate::state::{RulerState, ShapesPoolMutRef, ShapesPoolRef};
|
||||
use crate::tiles::{self, PendingTiles, Tile, TileRect, TileViewbox, TileHashMap};
|
||||
use crate::view::{Viewbox, ViewboxUpdated};
|
||||
use crate::wapi;
|
||||
use crate::{get_gpu_state, performance};
|
||||
@ -66,12 +66,62 @@ pub enum RenderFlag {
|
||||
Full = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum TileDisplayPhase {
|
||||
Enter = 0,
|
||||
Exit = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TileDisplayItem {
|
||||
pub id: Uuid,
|
||||
pub phase: TileDisplayPhase,
|
||||
}
|
||||
|
||||
impl TileDisplayItem {
|
||||
pub fn get_display_list(shapes: ShapesPoolRef, viewbox: &Viewbox, root_id: Uuid, output: &mut HashMap<Tile, Vec<Self>>) {
|
||||
Self::dfs(shapes, viewbox, root_id, output);
|
||||
}
|
||||
|
||||
// Recursive helper that pushes to the output vector
|
||||
fn dfs(shapes: ShapesPoolRef, viewbox: &Viewbox, id: Uuid, output: &mut HashMap<Tile, Vec<Self>>) {
|
||||
if id.is_nil() {
|
||||
let shape = shapes.get(&id).unwrap();
|
||||
for shape_id in shape.children_ids(false) {
|
||||
Self::dfs(shapes, viewbox, shape_id, output);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let shape = shapes.get(&id).unwrap();
|
||||
let tile_rect = shape.get_tile_rect(&viewbox);
|
||||
for tile in tile_rect.iter(true) {
|
||||
let _ = output.entry(tile).or_insert_with(Vec::new);
|
||||
output.get_mut(&tile).unwrap().push(Self::enter(shape.id));
|
||||
for shape_id in shape.children_ids(false) {
|
||||
Self::dfs(shapes, viewbox, shape_id, output);
|
||||
}
|
||||
output.get_mut(&tile).unwrap().push(Self::exit(shape.id));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter(id: Uuid) -> Self {
|
||||
Self { id, phase: TileDisplayPhase::Enter }
|
||||
}
|
||||
|
||||
pub fn exit(id: Uuid) -> Self {
|
||||
Self { id, phase: TileDisplayPhase::Exit }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TileRenderState {
|
||||
pub current: Option<tiles::Tile>,
|
||||
pub current: Option<Tile>,
|
||||
pub current_had_shapes: bool,
|
||||
pub viewbox: tiles::TileViewbox,
|
||||
pub tiles: tiles::TileHashMap,
|
||||
pub viewbox: TileViewbox,
|
||||
pub tiles: TileHashMap,
|
||||
pub pending: PendingTiles,
|
||||
pub display_list: HashMap<Tile, Vec<TileDisplayItem>>,
|
||||
}
|
||||
|
||||
pub(crate) struct RenderState {
|
||||
@ -168,6 +218,7 @@ impl RenderState {
|
||||
options.dpr_viewport_interest_area_threshold,
|
||||
),
|
||||
pending: PendingTiles::new(),
|
||||
display_list: HashMap::new(),
|
||||
},
|
||||
nested_fills: vec![],
|
||||
nested_blurs: vec![],
|
||||
@ -591,7 +642,7 @@ impl RenderState {
|
||||
// gestures + pan/zoom). AA edge sampling is per-pixel and adds
|
||||
// up across many shapes; reverts to full quality on commit.
|
||||
let antialias = true;
|
||||
// && shape.should_use_antialias(self.get_scale_fast(), self.options.antialias_threshold);
|
||||
// && shape.should_use_antialias(self.viewbox.get_scale(), self.options.antialias_threshold);
|
||||
let skip_effects = false;
|
||||
|
||||
let has_nested_fills = self
|
||||
@ -661,7 +712,7 @@ impl RenderState {
|
||||
// );
|
||||
|
||||
if can_render_directly {
|
||||
let scale = self.get_scale_fast();
|
||||
let scale = self.viewbox.get_scale();
|
||||
let translation = self
|
||||
.surfaces
|
||||
.get_render_context_translation(self.render_area, scale);
|
||||
@ -717,7 +768,7 @@ impl RenderState {
|
||||
|
||||
// set clipping
|
||||
if let Some(clips) = clip_bounds.as_ref() {
|
||||
let scale = self.get_scale_fast();
|
||||
let scale = self.viewbox.get_scale();
|
||||
for (mut bounds, corners, transform, _inverse_transform) in clips.iter() {
|
||||
self.surfaces.apply_mut(surface_ids, |s| {
|
||||
s.canvas().concat(transform);
|
||||
@ -858,7 +909,7 @@ impl RenderState {
|
||||
let count_inner_strokes = shape.count_visible_inner_strokes();
|
||||
// Erode the main text fill by 1px when there are inner strokes, to avoid a visible seam at the glyph edge.
|
||||
let text_fill_inset =
|
||||
(count_inner_strokes > 0).then(|| 1.0 / self.get_scale_fast());
|
||||
(count_inner_strokes > 0).then(|| 1.0 / self.viewbox.get_scale());
|
||||
let text_stroke_blur_outset =
|
||||
Stroke::max_bounds_width(shape.visible_strokes(), false);
|
||||
let mut paragraph_builders = text_content.paragraph_builder_group_from_text(None);
|
||||
@ -1262,8 +1313,22 @@ impl RenderState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn populate_pending_nodes(&mut self) {
|
||||
fn populate_tile_pending_nodes(&mut self) -> Result<()> {
|
||||
println!("populate_tile_pending_nodes");
|
||||
let design_state = get_design_state();
|
||||
let tree = &design_state.shapes;
|
||||
|
||||
// Clears tile pending nodes.
|
||||
self.tile.display_list.clear();
|
||||
TileDisplayItem::get_display_list(
|
||||
tree,
|
||||
&self.viewbox,
|
||||
Uuid::nil(),
|
||||
&mut self.tile.display_list
|
||||
);
|
||||
|
||||
// println!("display_list {:?}", self.tile.display_list);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clears all the necessary vecs and hashmaps.
|
||||
@ -1281,8 +1346,6 @@ impl RenderState {
|
||||
self.pending_nodes.clear();
|
||||
self.pending_nodes.reserve(tree.len());
|
||||
|
||||
self.populate_pending_nodes();
|
||||
|
||||
// Clear nested state stacks to avoid residual fills/blurs from previous renders
|
||||
// being incorrectly applied to new frames
|
||||
self.nested_fills.clear();
|
||||
@ -1293,6 +1356,9 @@ impl RenderState {
|
||||
// reorder by distance to the center.
|
||||
self.tile.current = None;
|
||||
|
||||
// WIP: Generamos la lista de nodos pendientes por cada tile.
|
||||
self.populate_tile_pending_nodes();
|
||||
|
||||
self.empty_grid_frame_ids.clear();
|
||||
if self.show_grid.is_some() {
|
||||
for shape in tree.iter() {
|
||||
@ -1545,6 +1611,7 @@ impl RenderState {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn render_shape_enter(
|
||||
&mut self,
|
||||
element: &Shape,
|
||||
@ -1555,7 +1622,7 @@ impl RenderState {
|
||||
enter_exit::render_shape_enter(self, element, mask, clip_bounds, target_surface)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn render_shape_exit(
|
||||
&mut self,
|
||||
element: &Shape,
|
||||
@ -1643,6 +1710,51 @@ impl RenderState {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_shape_tree_tile(
|
||||
&mut self,
|
||||
timestamp: i32,
|
||||
allow_stop: bool,
|
||||
export: bool,
|
||||
) -> Result<(bool, bool)> {
|
||||
// Redirects writings.
|
||||
let mut target_surface = SurfaceId::Current;
|
||||
if export {
|
||||
target_surface = SurfaceId::Export;
|
||||
}
|
||||
|
||||
let design_state = get_design_state();
|
||||
let tree = &design_state.shapes;
|
||||
|
||||
let Some(current_tile) = self.tile.current else {
|
||||
return Ok((true, true));
|
||||
};
|
||||
|
||||
let Some(display_list) = self.tile.display_list.get_mut(¤t_tile) else {
|
||||
return Ok((true, true));
|
||||
};
|
||||
|
||||
let len = display_list.len();
|
||||
for index in 0..len {
|
||||
let display_item = &display_list[index];
|
||||
let Some(shape) = tree.get(&display_item.id) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
match display_item.phase {
|
||||
TileDisplayPhase::Enter => {
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_color(skia::Color::BLACK);
|
||||
self.surfaces.canvas(SurfaceId::Backbuffer).draw_rect(shape.selrect, &paint);
|
||||
}
|
||||
TileDisplayPhase::Exit => {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((false, false))
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn render_shape_tree_tile(
|
||||
&mut self,
|
||||
timestamp: i32,
|
||||
@ -1673,7 +1785,7 @@ impl RenderState {
|
||||
// Skip it for this pass; a subsequent render will pick it up once present.
|
||||
continue;
|
||||
};
|
||||
let scale = self.get_scale_fast();
|
||||
let scale = self.viewbox.get_scale();
|
||||
let mut extrect: Option<Rect> = None;
|
||||
|
||||
let Some(current_tile) = self.tile.current else {
|
||||
@ -1957,6 +2069,7 @@ impl RenderState {
|
||||
|
||||
Ok((is_empty, false))
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn render_shape_tree_tiled(
|
||||
&mut self,
|
||||
@ -2019,18 +2132,8 @@ impl RenderState {
|
||||
self.surfaces.draw_current_tile_into_tile_atlas(
|
||||
¤t_tile,
|
||||
);
|
||||
|
||||
if self.options.is_debug_visible() {
|
||||
debug::render_workspace_current_tile(
|
||||
self,
|
||||
"".to_string(),
|
||||
current_tile,
|
||||
tile_rect,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if self.tile.tiles.is_empty_at(current_tile) {
|
||||
// println!("self.tile.tiles.is_empty_at({:?}", current_tile);
|
||||
self.surfaces.remove_cached_tile_surface(current_tile);
|
||||
}
|
||||
}
|
||||
@ -2063,6 +2166,7 @@ impl RenderState {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
// Check if any shape on this tile has a background blur.
|
||||
// If so, we need ALL root shapes rendered (not just those
|
||||
// assigned to this tile) because the blur snapshots Current
|
||||
@ -2104,6 +2208,7 @@ impl RenderState {
|
||||
mask: false,
|
||||
flattened: false,
|
||||
}));
|
||||
*/
|
||||
} else {
|
||||
// If there are no more pending tiles, stop.
|
||||
should_stop = true;
|
||||
@ -2116,10 +2221,6 @@ impl RenderState {
|
||||
Ok(FrameType::Full)
|
||||
}
|
||||
|
||||
pub fn render_shape_tree() {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a shape returns the TileRect with the range of tiles that the shape is in.
|
||||
* This is always limited to the interest area to optimize performance and prevent
|
||||
@ -2273,7 +2374,7 @@ impl RenderState {
|
||||
let design_state = get_design_state();
|
||||
let tree = &design_state.shapes;
|
||||
|
||||
let zoom_changed = self.zoom_changed();
|
||||
let zoom_changed = self.viewbox.is_zoom_changed();
|
||||
performance::begin_measure!("rebuild_tile_index");
|
||||
let mut nodes = Vec::<Uuid>::with_capacity(64);
|
||||
nodes.push(Uuid::nil());
|
||||
@ -2304,7 +2405,7 @@ impl RenderState {
|
||||
// Zoom changes world tile size: a partial cache update would mix scales in the
|
||||
// mosaic and glitch. Same zoom as last finished render (typical pan): drop only
|
||||
// tile textures and keep the cache canvas for render_from_cache.
|
||||
if !self.zoom_changed() {
|
||||
if !self.viewbox.is_zoom_changed() {
|
||||
self.surfaces.invalidate_tile_cache();
|
||||
}
|
||||
|
||||
@ -2438,17 +2539,6 @@ impl RenderState {
|
||||
self.viewbox.get_scale()
|
||||
}
|
||||
|
||||
/// Hot-path variant that skips the export_context check.
|
||||
/// Use in render_shape / walk loops where export is never active.
|
||||
#[inline]
|
||||
pub fn get_scale_fast(&self) -> f32 {
|
||||
self.viewbox.get_scale()
|
||||
}
|
||||
|
||||
pub fn zoom_changed(&self) -> bool {
|
||||
self.viewbox.is_zoom_changed()
|
||||
}
|
||||
|
||||
pub fn mark_touched(&mut self, uuid: Uuid) {
|
||||
self.touched_ids.insert(uuid);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ use crate::shapes::{
|
||||
ConstraintH, ConstraintV, Frame, Group, GrowType, Layout, Modifier, Shape, TransformEntry,
|
||||
TransformEntrySource, Type,
|
||||
};
|
||||
use crate::state::{ShapesPoolRef, State};
|
||||
use crate::state::{ShapesPoolRef, DesignState};
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -176,7 +176,7 @@ fn set_pixel_precision(transform: &mut Matrix, bounds: &mut Bounds) {
|
||||
fn propagate_transform(
|
||||
entry: TransformEntry,
|
||||
pixel_precision: bool,
|
||||
state: &State,
|
||||
state: &DesignState,
|
||||
entries: &mut VecDeque<Modifier>,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
modifiers: &mut HashMap<Uuid, Matrix>,
|
||||
@ -324,7 +324,7 @@ fn propagate_transform(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn propagate_reflow(
|
||||
id: &Uuid,
|
||||
state: &State,
|
||||
state: &DesignState,
|
||||
entries: &mut VecDeque<Modifier>,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
layout_reflows: &mut HashSet<Uuid>,
|
||||
@ -380,7 +380,7 @@ fn propagate_reflow(
|
||||
|
||||
fn reflow_shape(
|
||||
id: &Uuid,
|
||||
state: &State,
|
||||
state: &DesignState,
|
||||
reflown: &mut HashSet<Uuid>,
|
||||
entries: &mut VecDeque<Modifier>,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
@ -409,7 +409,7 @@ fn reflow_shape(
|
||||
}
|
||||
|
||||
pub fn propagate_modifiers(
|
||||
state: &State,
|
||||
state: &DesignState,
|
||||
modifiers: &[TransformEntry],
|
||||
pixel_precision: bool,
|
||||
) -> Result<Vec<TransformEntry>> {
|
||||
|
||||
@ -21,7 +21,7 @@ use crate::{get_render_state, tiles};
|
||||
/// It is created by [init] and passed to the other exported functions.
|
||||
/// Note that rust-skia data structures are not thread safe, so a state
|
||||
/// must not be shared between different Web Workers.
|
||||
pub(crate) struct State {
|
||||
pub(crate) struct DesignState {
|
||||
pub current_id: Option<Uuid>,
|
||||
pub current_browser: u8,
|
||||
pub shapes: ShapesPool,
|
||||
@ -30,7 +30,7 @@ pub(crate) struct State {
|
||||
pub loading: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
impl DesignState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_id: None,
|
||||
@ -317,4 +317,5 @@ impl State {
|
||||
render_state.mark_touched(id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,13 +2,13 @@ use crate::error::{Error, Result};
|
||||
use crate::get_render_state;
|
||||
use crate::mem;
|
||||
use crate::shapes::Fill;
|
||||
use crate::state::State;
|
||||
use crate::state::DesignState;
|
||||
use crate::uuid::Uuid;
|
||||
use crate::with_state;
|
||||
use crate::{shapes::ImageFill, utils::uuid_from_u32_quartet};
|
||||
use macros::wasm_error;
|
||||
|
||||
fn touch_shapes_with_image(state: &mut State, image_id: Uuid) {
|
||||
fn touch_shapes_with_image(state: &mut DesignState, image_id: Uuid) {
|
||||
let ids: Vec<Uuid> = state
|
||||
.shapes
|
||||
.iter()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user