summaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-07-21 20:35:02 +0200
committerLaurenz <laurmaedje@gmail.com>2021-07-25 01:03:38 +0200
commitadb71ee040752f9348b0c9a511b2ab7e3710cb80 (patch)
tree13578f67cf7361a03924b2a7f18d3d17103dea09 /src/layout
parent927f1154fa983a13eda050d716d04dead96bf8fb (diff)
Move and refactor
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/incremental.rs186
-rw-r--r--src/layout/mod.rs162
-rw-r--r--src/layout/par.rs2
-rw-r--r--src/layout/tree.rs158
4 files changed, 258 insertions, 250 deletions
diff --git a/src/layout/incremental.rs b/src/layout/incremental.rs
index ca4839b6..e4496f06 100644
--- a/src/layout/incremental.rs
+++ b/src/layout/incremental.rs
@@ -5,14 +5,16 @@ use std::ops::Deref;
use super::*;
/// Caches layouting artifacts.
-#[derive(Default, Debug, Clone)]
+///
+/// _This is only available when the `layout-cache` feature is enabled._
#[cfg(feature = "layout-cache")]
+#[derive(Default, Debug, Clone)]
pub struct LayoutCache {
/// Maps from node hashes to the resulting frames and regions in which the
/// frames are valid. The right hand side of the hash map is a vector of
/// results because across one or more compilations, multiple different
/// layouts of the same node may have been requested.
- pub frames: HashMap<u64, Vec<FramesEntry>>,
+ frames: HashMap<u64, Vec<FramesEntry>>,
/// In how many compilations this cache has been used.
age: usize,
}
@@ -24,67 +26,42 @@ impl LayoutCache {
Self::default()
}
- /// Clear the cache.
- pub fn clear(&mut self) {
- self.frames.clear();
+ /// Whether the cache is empty.
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
}
/// Amount of items in the cache.
pub fn len(&self) -> usize {
- self.frames.iter().map(|(_, e)| e.len()).sum()
- }
-
- /// Retains all elements for which the closure on the level returns `true`.
- pub fn retain<F>(&mut self, mut f: F)
- where
- F: FnMut(usize) -> bool,
- {
- for (_, entries) in self.frames.iter_mut() {
- entries.retain(|entry| f(entry.level));
- }
+ self.frames.values().map(Vec::len).sum()
}
- /// Prepare the cache for the next round of compilation
- pub fn turnaround(&mut self) {
- self.age += 1;
- for entry in self.frames.iter_mut().flat_map(|(_, x)| x.iter_mut()) {
- for i in 0 .. (entry.temperature.len() - 1) {
- entry.temperature[i + 1] = entry.temperature[i];
- }
- entry.temperature[0] = 0;
- entry.age += 1;
- }
+ /// The number of levels stored in the cache.
+ pub fn levels(&self) -> usize {
+ self.entries().map(|entry| entry.level + 1).max().unwrap_or(0)
}
- /// The amount of levels stored in the cache.
- pub fn levels(&self) -> usize {
- self.frames
- .iter()
- .flat_map(|(_, x)| x)
- .map(|entry| entry.level + 1)
- .max()
- .unwrap_or(0)
+ /// An iterator over all entries in the cache.
+ pub fn entries(&self) -> impl Iterator<Item = &FramesEntry> + '_ {
+ self.frames.values().flatten()
}
- /// Fetches the appropriate entry from the cache if there is any.
+ /// Fetch matching cached frames if there are any.
pub fn get(
&mut self,
hash: u64,
regions: Regions,
) -> Option<Vec<Constrained<Rc<Frame>>>> {
- self.frames.get_mut(&hash).and_then(|frames| {
- for frame in frames {
- let res = frame.check(regions.clone());
- if res.is_some() {
- return res;
- }
+ let entries = self.frames.get_mut(&hash)?;
+ for entry in entries {
+ if let Some(frames) = entry.check(regions.clone()) {
+ return Some(frames);
}
-
- None
- })
+ }
+ None
}
- /// Inserts a new frame set into the cache.
+ /// Insert a new frame entry into the cache.
pub fn insert(
&mut self,
hash: u64,
@@ -99,16 +76,45 @@ impl LayoutCache {
}
}
}
+
+ /// Clear the cache.
+ pub fn clear(&mut self) {
+ self.frames.clear();
+ }
+
+ /// Retain all elements for which the closure on the level returns `true`.
+ pub fn retain<F>(&mut self, mut f: F)
+ where
+ F: FnMut(usize) -> bool,
+ {
+ for entries in self.frames.values_mut() {
+ entries.retain(|entry| f(entry.level));
+ }
+ }
+
+ /// Prepare the cache for the next round of compilation.
+ pub fn turnaround(&mut self) {
+ self.age += 1;
+ for entry in self.frames.values_mut().flatten() {
+ for i in 0 .. (entry.temperature.len() - 1) {
+ entry.temperature[i + 1] = entry.temperature[i];
+ }
+ entry.temperature[0] = 0;
+ entry.age += 1;
+ }
+ }
}
/// Cached frames from past layouting.
-#[derive(Debug, Clone)]
+///
+/// _This is only available when the `layout-cache` feature is enabled._
#[cfg(feature = "layout-cache")]
+#[derive(Debug, Clone)]
pub struct FramesEntry {
/// The cached frames for a node.
- pub frames: Vec<Constrained<Rc<Frame>>>,
+ frames: Vec<Constrained<Rc<Frame>>>,
/// How nested the frame was in the context is was originally appearing in.
- pub level: usize,
+ level: usize,
/// For how long the element already exists.
age: usize,
/// How much the element was accessed during the last five compilations, the
@@ -128,7 +134,8 @@ impl FramesEntry {
}
}
- /// Checks if the cached [`Frame`] is valid for the given regions.
+ /// Checks if the cached frames are valid in the given regions and returns
+ /// them if so.
pub fn check(&mut self, mut regions: Regions) -> Option<Vec<Constrained<Rc<Frame>>>> {
for (i, frame) in self.frames.iter().enumerate() {
if (i != 0 && !regions.next()) || !frame.constraints.check(&regions) {
@@ -137,18 +144,25 @@ impl FramesEntry {
}
self.temperature[0] += 1;
-
Some(self.frames.clone())
}
- /// Get the amount of compilation cycles this item has remained in the
- /// cache.
+ /// How nested the frame was in the context is was originally appearing in.
+ pub fn level(&self) -> usize {
+ self.level
+ }
+
+ /// The number of compilation cycles this item has remained in the cache.
pub fn age(&self) -> usize {
self.age
}
- /// Get the amount of consecutive cycles in which this item has not
- /// been used.
+ /// Whether this element was used in the last compilation cycle.
+ pub fn hit(&self) -> bool {
+ self.temperature[0] != 0
+ }
+
+ /// The amount of consecutive cycles in which this item has not been used.
pub fn cooldown(&self) -> usize {
let mut cycle = 0;
for &temp in &self.temperature[.. self.age] {
@@ -157,13 +171,23 @@ impl FramesEntry {
}
cycle += 1;
}
-
cycle
}
+}
- /// Whether this element was used in the last compilation cycle.
- pub fn hit(&self) -> bool {
- self.temperature[0] != 0
+/// Carries an item that only applies to certain regions and the constraints
+/// that describe these regions.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct Constrained<T> {
+ pub item: T,
+ pub constraints: Constraints,
+}
+
+impl<T> Deref for Constrained<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.item
}
}
@@ -194,6 +218,21 @@ impl Constraints {
}
}
+ #[cfg(feature = "layout-cache")]
+ fn check(&self, regions: &Regions) -> bool {
+ if self.expand != regions.expand {
+ return false;
+ }
+
+ let base = regions.base.to_spec();
+ let current = regions.current.to_spec();
+
+ current.eq_by(&self.min, |x, y| y.map_or(true, |y| x.fits(y)))
+ && current.eq_by(&self.max, |x, y| y.map_or(true, |y| x < &y))
+ && current.eq_by(&self.exact, |x, y| y.map_or(true, |y| x.approx_eq(y)))
+ && base.eq_by(&self.base, |x, y| y.map_or(true, |y| x.approx_eq(y)))
+ }
+
/// Set the appropriate base constraints for (relative) width and height
/// metrics, respectively.
pub fn set_base_using_linears(
@@ -210,21 +249,6 @@ impl Constraints {
}
}
- #[cfg(feature = "layout-cache")]
- fn check(&self, regions: &Regions) -> bool {
- if self.expand != regions.expand {
- return false;
- }
-
- let base = regions.base.to_spec();
- let current = regions.current.to_spec();
-
- current.eq_by(&self.min, |x, y| y.map_or(true, |y| x.fits(y)))
- && current.eq_by(&self.max, |x, y| y.map_or(true, |y| x < &y))
- && current.eq_by(&self.exact, |x, y| y.map_or(true, |y| x.approx_eq(y)))
- && base.eq_by(&self.base, |x, y| y.map_or(true, |y| x.approx_eq(y)))
- }
-
/// Changes all constraints by adding the `size` to them if they are `Some`.
pub fn mutate(&mut self, size: Size, regions: &Regions) {
for spec in [
@@ -251,22 +275,6 @@ impl Constraints {
}
}
-/// Carries an item that only applies to certain regions and the constraints
-/// that describe these regions.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct Constrained<T> {
- pub item: T,
- pub constraints: Constraints,
-}
-
-impl<T> Deref for Constrained<T> {
- type Target = T;
-
- fn deref(&self) -> &Self::Target {
- &self.item
- }
-}
-
/// Extends length-related options by providing convenience methods for setting
/// minimum and maximum lengths on them, even if they are `None`.
pub trait OptionExt {
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 7f8ee4ff..22e6e8c8 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -10,6 +10,7 @@ mod pad;
mod par;
mod shaping;
mod stack;
+mod tree;
pub use self::image::*;
pub use background::*;
@@ -21,21 +22,16 @@ pub use pad::*;
pub use par::*;
pub use shaping::*;
pub use stack::*;
+pub use tree::*;
-use std::any::Any;
-use std::fmt::{self, Debug, Formatter};
use std::hash::Hash;
#[cfg(feature = "layout-cache")]
use std::hash::Hasher;
use std::rc::Rc;
-#[cfg(feature = "layout-cache")]
-use fxhash::FxHasher64;
-
use crate::font::FontCache;
use crate::geom::*;
use crate::image::ImageCache;
-use crate::loading::Loader;
use crate::Context;
/// Layout a tree into a collection of frames.
@@ -44,157 +40,6 @@ pub fn layout(ctx: &mut Context, tree: &LayoutTree) -> Vec<Rc<Frame>> {
tree.layout(&mut ctx)
}
-/// A tree of layout nodes.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct LayoutTree {
- /// Runs of pages with the same properties.
- pub runs: Vec<PageRun>,
-}
-
-impl LayoutTree {
- /// Layout the tree into a collection of frames.
- pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
- self.runs.iter().flat_map(|run| run.layout(ctx)).collect()
- }
-}
-
-/// A run of pages that all have the same properties.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct PageRun {
- /// The size of each page.
- pub size: Size,
- /// The layout node that produces the actual pages (typically a
- /// [`StackNode`]).
- pub child: LayoutNode,
-}
-
-impl PageRun {
- /// Layout the page run.
- pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
- // When one of the lengths is infinite the page fits its content along
- // that axis.
- let Size { width, height } = self.size;
- let expand = Spec::new(width.is_finite(), height.is_finite());
- let regions = Regions::repeat(self.size, expand);
- self.child.layout(ctx, &regions).into_iter().map(|c| c.item).collect()
- }
-}
-
-/// A dynamic layouting node.
-pub struct LayoutNode {
- node: Box<dyn Bounds>,
- #[cfg(feature = "layout-cache")]
- hash: u64,
-}
-
-impl LayoutNode {
- /// Create a new instance from any node that satisifies the required bounds.
- #[cfg(feature = "layout-cache")]
- pub fn new<T>(node: T) -> Self
- where
- T: Layout + Debug + Clone + Eq + PartialEq + Hash + 'static,
- {
- let hash = {
- let mut state = FxHasher64::default();
- node.type_id().hash(&mut state);
- node.hash(&mut state);
- state.finish()
- };
-
- Self { node: Box::new(node), hash }
- }
-
- /// Create a new instance from any node that satisifies the required bounds.
- #[cfg(not(feature = "layout-cache"))]
- pub fn new<T>(node: T) -> Self
- where
- T: Layout + Debug + Clone + Eq + PartialEq + 'static,
- {
- Self { node: Box::new(node) }
- }
-}
-
-impl Layout for LayoutNode {
- fn layout(
- &self,
- ctx: &mut LayoutContext,
- regions: &Regions,
- ) -> Vec<Constrained<Rc<Frame>>> {
- #[cfg(feature = "layout-cache")]
- {
- ctx.level += 1;
- let frames = ctx.layouts.get(self.hash, regions.clone()).unwrap_or_else(|| {
- let frames = self.node.layout(ctx, regions);
- ctx.layouts.insert(self.hash, frames.clone(), ctx.level - 1);
- frames
- });
- ctx.level -= 1;
- frames
- }
-
- #[cfg(not(feature = "layout-cache"))]
- self.node.layout(ctx, regions)
- }
-}
-
-impl Debug for LayoutNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.node.fmt(f)
- }
-}
-
-impl Clone for LayoutNode {
- fn clone(&self) -> Self {
- Self {
- node: self.node.dyn_clone(),
- #[cfg(feature = "layout-cache")]
- hash: self.hash,
- }
- }
-}
-
-impl Eq for LayoutNode {}
-
-impl PartialEq for LayoutNode {
- fn eq(&self, other: &Self) -> bool {
- self.node.dyn_eq(other.node.as_ref())
- }
-}
-
-#[cfg(feature = "layout-cache")]
-impl Hash for LayoutNode {
- fn hash<H: Hasher>(&self, state: &mut H) {
- state.write_u64(self.hash);
- }
-}
-
-trait Bounds: Layout + Debug + 'static {
- fn as_any(&self) -> &dyn Any;
- fn dyn_eq(&self, other: &dyn Bounds) -> bool;
- fn dyn_clone(&self) -> Box<dyn Bounds>;
-}
-
-impl<T> Bounds for T
-where
- T: Layout + Debug + Eq + PartialEq + Clone + 'static,
-{
- fn as_any(&self) -> &dyn Any {
- self
- }
-
- fn dyn_eq(&self, other: &dyn Bounds) -> bool {
- if let Some(other) = other.as_any().downcast_ref::<Self>() {
- self == other
- } else {
- false
- }
- }
-
- fn dyn_clone(&self) -> Box<dyn Bounds> {
- Box::new(self.clone())
- }
-}
-
/// Layout a node.
pub trait Layout {
/// Layout the node into the given regions.
@@ -207,8 +52,6 @@ pub trait Layout {
/// The context for layouting.
pub struct LayoutContext<'a> {
- /// The loader from which fonts are loaded.
- pub loader: &'a dyn Loader,
/// The cache for parsed font faces.
pub fonts: &'a mut FontCache,
/// The cache for decoded imges.
@@ -225,7 +68,6 @@ impl<'a> LayoutContext<'a> {
/// Create a new layout context.
pub fn new(ctx: &'a mut Context) -> Self {
Self {
- loader: ctx.loader.as_ref(),
fonts: &mut ctx.fonts,
images: &mut ctx.images,
#[cfg(feature = "layout-cache")]
diff --git a/src/layout/par.rs b/src/layout/par.rs
index 72b3edfb..8736452b 100644
--- a/src/layout/par.rs
+++ b/src/layout/par.rs
@@ -47,7 +47,7 @@ impl Layout for ParNode {
// Find out the BiDi embedding levels.
let bidi = BidiInfo::new(&text, Level::from_dir(self.dir));
- // Prepare paragraph layout by bulding a representation on which we can
+ // Prepare paragraph layout by building a representation on which we can
// do line breaking without layouting each and every line from scratch.
let layouter = ParLayouter::new(self, ctx, regions, bidi);
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
new file mode 100644
index 00000000..258f1ccc
--- /dev/null
+++ b/src/layout/tree.rs
@@ -0,0 +1,158 @@
+use super::*;
+
+use std::any::Any;
+use std::fmt::{self, Debug, Formatter};
+
+#[cfg(feature = "layout-cache")]
+use fxhash::FxHasher64;
+
+/// A tree of layout nodes.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct LayoutTree {
+ /// Runs of pages with the same properties.
+ pub runs: Vec<PageRun>,
+}
+
+impl LayoutTree {
+ /// Layout the tree into a collection of frames.
+ pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
+ self.runs.iter().flat_map(|run| run.layout(ctx)).collect()
+ }
+}
+
+/// A run of pages that all have the same properties.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct PageRun {
+ /// The size of each page.
+ pub size: Size,
+ /// The layout node that produces the actual pages (typically a
+ /// [`StackNode`]).
+ pub child: LayoutNode,
+}
+
+impl PageRun {
+ /// Layout the page run.
+ pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
+ // When one of the lengths is infinite the page fits its content along
+ // that axis.
+ let Size { width, height } = self.size;
+ let expand = Spec::new(width.is_finite(), height.is_finite());
+ let regions = Regions::repeat(self.size, expand);
+ self.child.layout(ctx, &regions).into_iter().map(|c| c.item).collect()
+ }
+}
+
+/// A dynamic layouting node.
+pub struct LayoutNode {
+ node: Box<dyn Bounds>,
+ #[cfg(feature = "layout-cache")]
+ hash: u64,
+}
+
+impl LayoutNode {
+ /// Create a new instance from any node that satisifies the required bounds.
+ #[cfg(feature = "layout-cache")]
+ pub fn new<T>(node: T) -> Self
+ where
+ T: Layout + Debug + Clone + Eq + PartialEq + Hash + 'static,
+ {
+ let hash = {
+ let mut state = FxHasher64::default();
+ node.type_id().hash(&mut state);
+ node.hash(&mut state);
+ state.finish()
+ };
+
+ Self { node: Box::new(node), hash }
+ }
+
+ /// Create a new instance from any node that satisifies the required bounds.
+ #[cfg(not(feature = "layout-cache"))]
+ pub fn new<T>(node: T) -> Self
+ where
+ T: Layout + Debug + Clone + Eq + PartialEq + 'static,
+ {
+ Self { node: Box::new(node) }
+ }
+}
+
+impl Layout for LayoutNode {
+ fn layout(
+ &self,
+ ctx: &mut LayoutContext,
+ regions: &Regions,
+ ) -> Vec<Constrained<Rc<Frame>>> {
+ #[cfg(feature = "layout-cache")]
+ {
+ ctx.level += 1;
+ let frames = ctx.layouts.get(self.hash, regions.clone()).unwrap_or_else(|| {
+ let frames = self.node.layout(ctx, regions);
+ ctx.layouts.insert(self.hash, frames.clone(), ctx.level - 1);
+ frames
+ });
+ ctx.level -= 1;
+ frames
+ }
+
+ #[cfg(not(feature = "layout-cache"))]
+ self.node.layout(ctx, regions)
+ }
+}
+
+impl Debug for LayoutNode {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ self.node.fmt(f)
+ }
+}
+
+impl Clone for LayoutNode {
+ fn clone(&self) -> Self {
+ Self {
+ node: self.node.dyn_clone(),
+ #[cfg(feature = "layout-cache")]
+ hash: self.hash,
+ }
+ }
+}
+
+impl Eq for LayoutNode {}
+
+impl PartialEq for LayoutNode {
+ fn eq(&self, other: &Self) -> bool {
+ self.node.dyn_eq(other.node.as_ref())
+ }
+}
+
+#[cfg(feature = "layout-cache")]
+impl Hash for LayoutNode {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ state.write_u64(self.hash);
+ }
+}
+
+trait Bounds: Layout + Debug + 'static {
+ fn as_any(&self) -> &dyn Any;
+ fn dyn_eq(&self, other: &dyn Bounds) -> bool;
+ fn dyn_clone(&self) -> Box<dyn Bounds>;
+}
+
+impl<T> Bounds for T
+where
+ T: Layout + Debug + Eq + PartialEq + Clone + 'static,
+{
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+
+ fn dyn_eq(&self, other: &dyn Bounds) -> bool {
+ if let Some(other) = other.as_any().downcast_ref::<Self>() {
+ self == other
+ } else {
+ false
+ }
+ }
+
+ fn dyn_clone(&self) -> Box<dyn Bounds> {
+ Box::new(self.clone())
+ }
+}