mirror of
https://github.com/penpot/penpot.git
synced 2026-05-25 09:53:44 +00:00
♻️ Refactor how viewport interest area works
This commit is contained in:
parent
03487f90e5
commit
9fccee8689
@ -525,7 +525,7 @@ impl RenderState {
|
|||||||
tiles,
|
tiles,
|
||||||
tile_viewbox: tiles::TileViewbox::new_with_interest(
|
tile_viewbox: tiles::TileViewbox::new_with_interest(
|
||||||
viewbox,
|
viewbox,
|
||||||
options.viewport_interest_area_threshold,
|
options.dpr_viewport_interest_area_threshold,
|
||||||
1.0,
|
1.0,
|
||||||
),
|
),
|
||||||
pending_tiles: PendingTiles::new_empty(),
|
pending_tiles: PendingTiles::new_empty(),
|
||||||
@ -742,8 +742,11 @@ impl RenderState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_dpr(&mut self, dpr: f32) -> Result<()> {
|
pub fn set_dpr(&mut self, dpr: f32) -> Result<()> {
|
||||||
if Some(dpr) != self.options.dpr {
|
// Only when this function returns true (it means the value
|
||||||
self.options.dpr = Some(dpr);
|
// was properly changed) the rest of the functions is called.
|
||||||
|
if self.options.set_dpr(dpr) {
|
||||||
|
self.tile_viewbox
|
||||||
|
.set_interest(self.options.dpr_viewport_interest_area_threshold);
|
||||||
self.resize(
|
self.resize(
|
||||||
self.viewbox.width.floor() as i32,
|
self.viewbox.width.floor() as i32,
|
||||||
self.viewbox.height.floor() as i32,
|
self.viewbox.height.floor() as i32,
|
||||||
@ -758,11 +761,15 @@ impl RenderState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_viewport_interest_area_threshold(&mut self, value: i32) {
|
pub fn set_viewport_interest_area_threshold(&mut self, value: i32) {
|
||||||
self.options.set_viewport_interest_area_threshold(value);
|
// Only when this function returns true (it means the value
|
||||||
// The TileViewbox stores its own copy of `interest` (set at
|
// was changed properly) the tile_viewbox.set_interest is called.
|
||||||
// construction). Without propagating, options change wouldn't
|
if self.options.set_viewport_interest_area_threshold(value) {
|
||||||
// affect pending_tiles generation.
|
// The TileViewbox stores its own copy of `interest` (set at
|
||||||
self.tile_viewbox.set_interest(value);
|
// construction). Without propagating, options change wouldn't
|
||||||
|
// affect pending_tiles generation.
|
||||||
|
self.tile_viewbox
|
||||||
|
.set_interest(self.options.dpr_viewport_interest_area_threshold);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_node_batch_threshold(&mut self, value: i32) {
|
pub fn set_node_batch_threshold(&mut self, value: i32) {
|
||||||
@ -786,8 +793,8 @@ impl RenderState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, width: i32, height: i32) -> Result<()> {
|
pub fn resize(&mut self, width: i32, height: i32) -> Result<()> {
|
||||||
let dpr_width = (width as f32 * self.options.dpr()).floor() as i32;
|
let dpr_width = (width as f32 * self.options.dpr).floor() as i32;
|
||||||
let dpr_height = (height as f32 * self.options.dpr()).floor() as i32;
|
let dpr_height = (height as f32 * self.options.dpr).floor() as i32;
|
||||||
self.surfaces
|
self.surfaces
|
||||||
.resize(&mut self.gpu_state, dpr_width, dpr_height)?;
|
.resize(&mut self.gpu_state, dpr_width, dpr_height)?;
|
||||||
self.viewbox.set_wh(width as f32, height as f32);
|
self.viewbox.set_wh(width as f32, height as f32);
|
||||||
@ -1739,7 +1746,7 @@ impl RenderState {
|
|||||||
// and drawing from it avoids mixing a partially-updated Cache surface with missing tiles.
|
// and drawing from it avoids mixing a partially-updated Cache surface with missing tiles.
|
||||||
if self.options.is_fast_mode() && self.render_in_progress && self.surfaces.has_atlas() {
|
if self.options.is_fast_mode() && self.render_in_progress && self.surfaces.has_atlas() {
|
||||||
self.surfaces
|
self.surfaces
|
||||||
.draw_atlas_to_target(self.viewbox, self.options.dpr(), bg_color);
|
.draw_atlas_to_target(self.viewbox, self.options.dpr, bg_color);
|
||||||
|
|
||||||
if self.options.is_debug_visible() {
|
if self.options.is_debug_visible() {
|
||||||
debug::render(self);
|
debug::render(self);
|
||||||
@ -1759,15 +1766,15 @@ impl RenderState {
|
|||||||
// Scale and translate the target according to the cached data
|
// Scale and translate the target according to the cached data
|
||||||
let navigate_zoom = self.viewbox.zoom / self.cached_viewbox.zoom;
|
let navigate_zoom = self.viewbox.zoom / self.cached_viewbox.zoom;
|
||||||
|
|
||||||
let interest = self.options.viewport_interest_area_threshold;
|
let interest = self.options.dpr_viewport_interest_area_threshold;
|
||||||
let TileRect(start_tile_x, start_tile_y, _, _) =
|
let TileRect(start_tile_x, start_tile_y, _, _) =
|
||||||
tiles::get_tiles_for_viewbox_with_interest(
|
tiles::get_tiles_for_viewbox_with_interest(
|
||||||
self.cached_viewbox,
|
self.cached_viewbox,
|
||||||
interest,
|
interest,
|
||||||
cached_scale,
|
cached_scale,
|
||||||
);
|
);
|
||||||
let offset_x = self.viewbox.area.left * self.cached_viewbox.zoom * self.options.dpr();
|
let offset_x = self.viewbox.area.left * self.cached_viewbox.zoom * self.options.dpr;
|
||||||
let offset_y = self.viewbox.area.top * self.cached_viewbox.zoom * self.options.dpr();
|
let offset_y = self.viewbox.area.top * self.cached_viewbox.zoom * self.options.dpr;
|
||||||
let translate_x = (start_tile_x as f32 * tiles::TILE_SIZE) - offset_x;
|
let translate_x = (start_tile_x as f32 * tiles::TILE_SIZE) - offset_x;
|
||||||
let translate_y = (start_tile_y as f32 * tiles::TILE_SIZE) - offset_y;
|
let translate_y = (start_tile_y as f32 * tiles::TILE_SIZE) - offset_y;
|
||||||
|
|
||||||
@ -1780,8 +1787,8 @@ impl RenderState {
|
|||||||
let cache_h = cache_dim.height as f32;
|
let cache_h = cache_dim.height as f32;
|
||||||
|
|
||||||
// Viewport in target pixels.
|
// Viewport in target pixels.
|
||||||
let vw = (self.viewbox.width * self.options.dpr()).max(1.0);
|
let vw = (self.viewbox.width * self.options.dpr).max(1.0);
|
||||||
let vh = (self.viewbox.height * self.options.dpr()).max(1.0);
|
let vh = (self.viewbox.height * self.options.dpr).max(1.0);
|
||||||
|
|
||||||
// Inverse-map viewport corners into cache coordinates.
|
// Inverse-map viewport corners into cache coordinates.
|
||||||
// target = (cache * navigate_zoom) translated by (translate_x, translate_y) (in cache coords).
|
// target = (cache * navigate_zoom) translated by (translate_x, translate_y) (in cache coords).
|
||||||
@ -1809,7 +1816,7 @@ impl RenderState {
|
|||||||
if self.surfaces.has_atlas() {
|
if self.surfaces.has_atlas() {
|
||||||
self.surfaces.draw_atlas_to_target(
|
self.surfaces.draw_atlas_to_target(
|
||||||
self.viewbox,
|
self.viewbox,
|
||||||
self.options.dpr(),
|
self.options.dpr,
|
||||||
bg_color,
|
bg_color,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1966,12 +1973,12 @@ impl RenderState {
|
|||||||
let viewbox_cache_size = get_cache_size(
|
let viewbox_cache_size = get_cache_size(
|
||||||
self.viewbox,
|
self.viewbox,
|
||||||
scale,
|
scale,
|
||||||
self.options.viewport_interest_area_threshold,
|
self.options.dpr_viewport_interest_area_threshold,
|
||||||
);
|
);
|
||||||
let cached_viewbox_cache_size = get_cache_size(
|
let cached_viewbox_cache_size = get_cache_size(
|
||||||
self.cached_viewbox,
|
self.cached_viewbox,
|
||||||
scale,
|
scale,
|
||||||
self.options.viewport_interest_area_threshold,
|
self.options.dpr_viewport_interest_area_threshold,
|
||||||
);
|
);
|
||||||
// Only resize cache if the new size is larger than the cached size
|
// Only resize cache if the new size is larger than the cached size
|
||||||
// This avoids unnecessary surface recreations when the cache size decreases
|
// This avoids unnecessary surface recreations when the cache size decreases
|
||||||
@ -1980,7 +1987,7 @@ impl RenderState {
|
|||||||
{
|
{
|
||||||
self.surfaces.resize_cache(
|
self.surfaces.resize_cache(
|
||||||
viewbox_cache_size,
|
viewbox_cache_size,
|
||||||
self.options.viewport_interest_area_threshold,
|
self.options.dpr_viewport_interest_area_threshold,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3750,11 +3757,11 @@ impl RenderState {
|
|||||||
if let Some((_, export_scale)) = self.export_context {
|
if let Some((_, export_scale)) = self.export_context {
|
||||||
return export_scale;
|
return export_scale;
|
||||||
}
|
}
|
||||||
self.viewbox.zoom() * self.options.dpr()
|
self.viewbox.zoom() * self.options.dpr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cached_scale(&self) -> f32 {
|
pub fn get_cached_scale(&self) -> f32 {
|
||||||
self.cached_viewbox.zoom() * self.options.dpr()
|
self.cached_viewbox.zoom() * self.options.dpr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zoom_changed(&self) -> bool {
|
pub fn zoom_changed(&self) -> bool {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ const SHOW_WASM_INFO: u32 = 0x08;
|
|||||||
// Render performance options
|
// Render performance options
|
||||||
// This is the extra area used for tile rendering (tiles beyond viewport).
|
// This is the extra area used for tile rendering (tiles beyond viewport).
|
||||||
// Higher values pre-render more tiles, reducing empty squares during pan but using more memory.
|
// Higher values pre-render more tiles, reducing empty squares during pan but using more memory.
|
||||||
const VIEWPORT_INTEREST_AREA_THRESHOLD: i32 = 3;
|
const VIEWPORT_INTEREST_AREA_THRESHOLD: i32 = 1;
|
||||||
const MAX_BLOCKING_TIME_MS: i32 = 32;
|
const MAX_BLOCKING_TIME_MS: i32 = 32;
|
||||||
const NODE_BATCH_THRESHOLD: i32 = 3;
|
const NODE_BATCH_THRESHOLD: i32 = 3;
|
||||||
const BLUR_DOWNSCALE_THRESHOLD: f32 = 8.0;
|
const BLUR_DOWNSCALE_THRESHOLD: f32 = 8.0;
|
||||||
@ -15,7 +15,7 @@ const ANTIALIAS_THRESHOLD: f32 = 7.0;
|
|||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct RenderOptions {
|
pub struct RenderOptions {
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
pub dpr: Option<f32>,
|
pub dpr: f32,
|
||||||
fast_mode: bool,
|
fast_mode: bool,
|
||||||
/// Active while the user is interacting with a shape (drag, resize,
|
/// Active while the user is interacting with a shape (drag, resize,
|
||||||
/// rotate). Implies `fast_mode` semantics for expensive effects but
|
/// rotate). Implies `fast_mode` semantics for expensive effects but
|
||||||
@ -25,6 +25,7 @@ pub struct RenderOptions {
|
|||||||
/// Minimum on-screen size (CSS px at 1:1 zoom) above which vector antialiasing is enabled.
|
/// Minimum on-screen size (CSS px at 1:1 zoom) above which vector antialiasing is enabled.
|
||||||
pub antialias_threshold: f32,
|
pub antialias_threshold: f32,
|
||||||
pub viewport_interest_area_threshold: i32,
|
pub viewport_interest_area_threshold: i32,
|
||||||
|
pub dpr_viewport_interest_area_threshold: i32,
|
||||||
pub max_blocking_time_ms: i32,
|
pub max_blocking_time_ms: i32,
|
||||||
pub node_batch_threshold: i32,
|
pub node_batch_threshold: i32,
|
||||||
pub blur_downscale_threshold: f32,
|
pub blur_downscale_threshold: f32,
|
||||||
@ -34,11 +35,12 @@ impl Default for RenderOptions {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
flags: 0,
|
flags: 0,
|
||||||
dpr: None,
|
dpr: 1.0,
|
||||||
fast_mode: false,
|
fast_mode: false,
|
||||||
interactive_transform: false,
|
interactive_transform: false,
|
||||||
antialias_threshold: ANTIALIAS_THRESHOLD,
|
antialias_threshold: ANTIALIAS_THRESHOLD,
|
||||||
viewport_interest_area_threshold: VIEWPORT_INTEREST_AREA_THRESHOLD,
|
viewport_interest_area_threshold: VIEWPORT_INTEREST_AREA_THRESHOLD,
|
||||||
|
dpr_viewport_interest_area_threshold: VIEWPORT_INTEREST_AREA_THRESHOLD,
|
||||||
max_blocking_time_ms: MAX_BLOCKING_TIME_MS,
|
max_blocking_time_ms: MAX_BLOCKING_TIME_MS,
|
||||||
node_batch_threshold: NODE_BATCH_THRESHOLD,
|
node_batch_threshold: NODE_BATCH_THRESHOLD,
|
||||||
blur_downscale_threshold: BLUR_DOWNSCALE_THRESHOLD,
|
blur_downscale_threshold: BLUR_DOWNSCALE_THRESHOLD,
|
||||||
@ -64,6 +66,24 @@ impl RenderOptions {
|
|||||||
self.fast_mode = enabled;
|
self.fast_mode = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates the dpr viewport interest area threshold.
|
||||||
|
/// This function is updated when the dpr or the
|
||||||
|
/// viewport_interest_area_threshold is changed
|
||||||
|
fn update_dpr_viewport_interest_area_threshold(&mut self) {
|
||||||
|
self.dpr_viewport_interest_area_threshold =
|
||||||
|
(self.dpr * self.viewport_interest_area_threshold as f32).ceil() as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the devicePixelRatio.
|
||||||
|
pub fn set_dpr(&mut self, value: f32) -> bool {
|
||||||
|
if value > 0.0 && self.dpr != value {
|
||||||
|
self.dpr = value;
|
||||||
|
self.update_dpr_viewport_interest_area_threshold();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Interactive transform is ON while the user is dragging, resizing
|
/// Interactive transform is ON while the user is dragging, resizing
|
||||||
/// or rotating a shape. Callers use it to keep per-frame flushing
|
/// or rotating a shape. Callers use it to keep per-frame flushing
|
||||||
/// enabled and to render visible tiles in a single frame so tiles
|
/// enabled and to render visible tiles in a single frame so tiles
|
||||||
@ -84,10 +104,6 @@ impl RenderOptions {
|
|||||||
self.fast_mode && !self.interactive_transform
|
self.fast_mode && !self.interactive_transform
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dpr(&self) -> f32 {
|
|
||||||
self.dpr.unwrap_or(1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_text_editor_v3(&self) -> bool {
|
pub fn is_text_editor_v3(&self) -> bool {
|
||||||
self.flags & TEXT_EDITOR_V3 == TEXT_EDITOR_V3
|
self.flags & TEXT_EDITOR_V3 == TEXT_EDITOR_V3
|
||||||
}
|
}
|
||||||
@ -96,33 +112,44 @@ impl RenderOptions {
|
|||||||
self.flags & SHOW_WASM_INFO == SHOW_WASM_INFO
|
self.flags & SHOW_WASM_INFO == SHOW_WASM_INFO
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_antialias_threshold(&mut self, value: f32) {
|
pub fn set_antialias_threshold(&mut self, value: f32) -> bool {
|
||||||
if value.is_finite() && value > 0.0 {
|
if value.is_finite() && value > 0.0 {
|
||||||
self.antialias_threshold = value;
|
self.antialias_threshold = value;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_blur_downscale_threshold(&mut self, value: f32) {
|
pub fn set_blur_downscale_threshold(&mut self, value: f32) -> bool {
|
||||||
if value.is_finite() && value > 0.0 {
|
if value.is_finite() && value > 0.0 {
|
||||||
self.blur_downscale_threshold = value;
|
self.blur_downscale_threshold = value;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_viewport_interest_area_threshold(&mut self, value: i32) {
|
pub fn set_viewport_interest_area_threshold(&mut self, value: i32) -> bool {
|
||||||
if value >= 0 {
|
if value >= 0 && self.viewport_interest_area_threshold != value {
|
||||||
self.viewport_interest_area_threshold = value;
|
self.viewport_interest_area_threshold = value;
|
||||||
|
self.update_dpr_viewport_interest_area_threshold();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_node_batch_threshold(&mut self, value: i32) {
|
pub fn set_node_batch_threshold(&mut self, value: i32) -> bool {
|
||||||
if value > 0 {
|
if value > 0 {
|
||||||
self.node_batch_threshold = value;
|
self.node_batch_threshold = value;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_blocking_time_ms(&mut self, value: i32) {
|
pub fn set_max_blocking_time_ms(&mut self, value: i32) -> bool {
|
||||||
if value > 0 {
|
if value > 0 {
|
||||||
self.max_blocking_time_ms = value;
|
self.max_blocking_time_ms = value;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ pub fn render_overlay(
|
|||||||
};
|
};
|
||||||
|
|
||||||
canvas.save();
|
canvas.save();
|
||||||
let zoom = viewbox.zoom * options.dpr();
|
let zoom = viewbox.zoom * options.dpr;
|
||||||
canvas.scale((zoom, zoom));
|
canvas.scale((zoom, zoom));
|
||||||
canvas.translate((-viewbox.area.left, -viewbox.area.top));
|
canvas.translate((-viewbox.area.left, -viewbox.area.top));
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ pub fn render(render_state: &mut RenderState, shapes: ShapesPoolRef) {
|
|||||||
canvas.save();
|
canvas.save();
|
||||||
|
|
||||||
let viewbox = render_state.viewbox;
|
let viewbox = render_state.viewbox;
|
||||||
let zoom = viewbox.zoom * render_state.options.dpr();
|
let zoom = viewbox.zoom * render_state.options.dpr;
|
||||||
|
|
||||||
canvas.scale((zoom, zoom));
|
canvas.scale((zoom, zoom));
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user