summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Howell <michael@notriddle.com>2023-01-08 18:50:49 -0700
committerMarge Bot <marge-bot@gnome.org>2023-03-23 01:41:14 +0000
commiteff355439c6b1cde7ffcc8ee6f191eaa1389dd26 (patch)
treec0f45f2bafc47bacb505f5d154205e5e07dd2c24
parent6cb052042609a0cf0546f454f7496415912d632f (diff)
downloadlibrsvg-eff355439c6b1cde7ffcc8ee6f191eaa1389dd26.tar.gz
Layer abstraction
Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/816>
-rw-r--r--src/drawing_ctx.rs68
-rw-r--r--src/layout.rs27
-rw-r--r--src/shapes.rs13
-rw-r--r--src/text.rs158
4 files changed, 164 insertions, 102 deletions
diff --git a/src/drawing_ctx.rs b/src/drawing_ctx.rs
index e3b9649f..c9cc9232 100644
--- a/src/drawing_ctx.rs
+++ b/src/drawing_ctx.rs
@@ -26,7 +26,7 @@ use crate::filter::FilterValueList;
use crate::filters::{self, FilterSpec};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::gradient::{GradientVariant, SpreadMethod, UserSpaceGradient};
-use crate::layout::{Image, Shape, StackingContext, Stroke, Text, TextSpan};
+use crate::layout::{Image, Layer, LayerKind, Shape, StackingContext, Stroke, Text, TextSpan};
use crate::length::*;
use crate::marker;
use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
@@ -34,8 +34,8 @@ use crate::paint_server::{PaintSource, UserSpacePaintSource};
use crate::path_builder::*;
use crate::pattern::UserSpacePattern;
use crate::properties::{
- ClipRule, ComputedValues, FillRule, Filter, Isolation, MaskType, MixBlendMode, Opacity,
- Overflow, PaintTarget, ShapeRendering, StrokeLinecap, StrokeLinejoin, TextRendering,
+ ClipRule, ComputedValues, FillRule, Filter, MaskType, MixBlendMode, Opacity, Overflow,
+ PaintTarget, ShapeRendering, StrokeLinecap, StrokeLinejoin, TextRendering,
};
use crate::rect::{rect_to_transform, IRect, Rect};
use crate::session::Session;
@@ -358,11 +358,15 @@ impl DrawingCtx {
*self.initial_viewport.vbox
}
+ pub fn toplevel_transform(&self) -> Transform {
+ self.initial_viewport.transform.clone()
+ }
+
pub fn is_measuring(&self) -> bool {
self.measuring
}
- fn get_transform(&self) -> ValidTransform {
+ pub fn get_transform(&self) -> ValidTransform {
let t = Transform::from(self.cr.matrix());
ValidTransform::try_from(t)
.expect("Cairo should already have checked that its current transform is valid")
@@ -731,17 +735,7 @@ impl DrawingCtx {
&self.empty_bbox(),
)?;
- let should_isolate = match stacking_ctx.isolation {
- Isolation::Auto => {
- let is_opaque = approx_eq!(f64, opacity, 1.0);
- !(is_opaque
- && stacking_ctx.filter == Filter::None
- && stacking_ctx.mask.is_none()
- && stacking_ctx.mix_blend_mode == MixBlendMode::Normal
- && stacking_ctx.clip_in_object_space.is_none())
- }
- Isolation::Isolate => true,
- };
+ let should_isolate = stacking_ctx.should_isolate();
let res = if should_isolate {
// Compute our assortment of affines
@@ -1278,6 +1272,27 @@ impl DrawingCtx {
Ok(Some(Rect::new(x0, y0, x1, y1)))
}
+ pub fn draw_layer(
+ &mut self,
+ layer: &Layer,
+ acquired_nodes: &mut AcquiredNodes<'_>,
+ values: &ComputedValues,
+ clipping: bool,
+ ) -> Result<BoundingBox, RenderingError> {
+ match &layer.kind {
+ LayerKind::Shape(shape) => self.draw_shape(
+ &shape,
+ &layer.stacking_ctx,
+ acquired_nodes,
+ values,
+ clipping,
+ ),
+ LayerKind::Text(text) => {
+ self.draw_text(&text, &layer.stacking_ctx, acquired_nodes, values, clipping)
+ }
+ }
+ }
+
pub fn draw_shape(
&mut self,
shape: &Shape,
@@ -1545,17 +1560,28 @@ impl DrawingCtx {
pub fn draw_text(
&mut self,
text: &Text,
+ stacking_ctx: &StackingContext,
acquired_nodes: &mut AcquiredNodes<'_>,
+ values: &ComputedValues,
clipping: bool,
) -> Result<BoundingBox, RenderingError> {
- let mut bbox = self.empty_bbox();
+ self.with_discrete_layer(
+ stacking_ctx,
+ acquired_nodes,
+ values,
+ clipping,
+ None,
+ &mut |an, dc, _transform| {
+ let mut bbox = dc.empty_bbox();
- for span in &text.spans {
- let span_bbox = self.draw_text_span(span, acquired_nodes, clipping)?;
- bbox.insert(&span_bbox);
- }
+ for span in &text.spans {
+ let span_bbox = dc.draw_text_span(span, an, clipping)?;
+ bbox.insert(&span_bbox);
+ }
- Ok(bbox)
+ Ok(bbox)
+ },
+ )
}
pub fn get_snapshot(
diff --git a/src/layout.rs b/src/layout.rs
index 19d07baf..7f19f15a 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -5,6 +5,8 @@
use std::rc::Rc;
use std::sync::Arc;
+use float_cmp::approx_eq;
+
use crate::aspect_ratio::AspectRatio;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
@@ -55,6 +57,16 @@ pub struct StackingContext {
pub link_target: Option<String>,
}
+/// The item being rendered inside a stacking context.
+pub struct Layer {
+ pub kind: LayerKind,
+ pub stacking_ctx: StackingContext,
+}
+pub enum LayerKind {
+ Shape(Box<Shape>),
+ Text(Box<Text>),
+}
+
/// Stroke parameters in user-space coordinates.
pub struct Stroke {
pub width: f64,
@@ -241,6 +253,21 @@ impl StackingContext {
ctx.link_target = link_target;
ctx
}
+
+ pub fn should_isolate(&self) -> bool {
+ let Opacity(UnitInterval(opacity)) = self.opacity;
+ match self.isolation {
+ Isolation::Auto => {
+ let is_opaque = approx_eq!(f64, opacity, 1.0);
+ !(is_opaque
+ && self.filter == Filter::None
+ && self.mask.is_none()
+ && self.mix_blend_mode == MixBlendMode::Normal
+ && self.clip_in_object_space.is_none())
+ }
+ Isolation::Isolate => true,
+ }
+ }
}
impl Stroke {
diff --git a/src/shapes.rs b/src/shapes.rs
index e92d1e85..81571f39 100644
--- a/src/shapes.rs
+++ b/src/shapes.rs
@@ -12,7 +12,7 @@ use crate::drawing_ctx::DrawingCtx;
use crate::element::{set_attribute, ElementTrait};
use crate::error::*;
use crate::iri::Iri;
-use crate::layout::{Marker, Shape, StackingContext, Stroke};
+use crate::layout::{Layer, LayerKind, Marker, Shape, StackingContext, Stroke};
use crate::length::*;
use crate::node::{CascadedValues, Node, NodeBorrow};
use crate::parsers::{optional_comma, Parse, ParseValue};
@@ -121,7 +121,7 @@ fn draw_basic_shape(
let stroke_paint = stroke_paint.to_user_space(&extents, &view_params, values);
let fill_paint = fill_paint.to_user_space(&extents, &view_params, values);
- let shape = Shape {
+ let shape = Box::new(Shape {
path: shape_def.path,
extents,
is_visible,
@@ -135,7 +135,7 @@ fn draw_basic_shape(
marker_start,
marker_mid,
marker_end,
- };
+ });
let elt = node.borrow_element();
let stacking_ctx = StackingContext::new(
@@ -146,7 +146,12 @@ fn draw_basic_shape(
values,
);
- draw_ctx.draw_shape(&shape, &stacking_ctx, acquired_nodes, values, clipping)
+ let layer = Layer {
+ kind: LayerKind::Shape(shape),
+ stacking_ctx,
+ };
+
+ draw_ctx.draw_layer(&layer, acquired_nodes, values, clipping)
}
macro_rules! impl_draw {
diff --git a/src/text.rs b/src/text.rs
index 8505492c..ca3598cf 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -12,7 +12,7 @@ use crate::document::{AcquiredNodes, NodeId};
use crate::drawing_ctx::{create_pango_context, DrawingCtx, FontOptions, ViewParams};
use crate::element::{set_attribute, ElementData, ElementTrait};
use crate::error::*;
-use crate::layout::{self, FontProperties, StackingContext, Stroke, TextSpan};
+use crate::layout::{self, FontProperties, Layer, LayerKind, StackingContext, Stroke, TextSpan};
use crate::length::*;
use crate::node::{CascadedValues, Node, NodeBorrow};
use crate::paint_server::PaintSource;
@@ -783,98 +783,102 @@ impl ElementTrait for Text {
values,
);
- draw_ctx.with_discrete_layer(
- &stacking_ctx,
- acquired_nodes,
- values,
- clipping,
- None,
- &mut |an, dc, transform| {
- let layout_context = LayoutContext {
- writing_mode: values.writing_mode(),
- transform: *transform,
- font_options: dc.get_font_options(),
- view_params: dc.get_view_params(),
- session: dc.session().clone(),
- };
+ let layout_text = {
+ let transform = if stacking_ctx.should_isolate() {
+ draw_ctx.toplevel_transform()
+ } else {
+ draw_ctx.get_transform().into::<Transform>()
+ };
- let mut x = self.x.to_user(&params);
- let mut y = self.y.to_user(&params);
+ let layout_context = LayoutContext {
+ writing_mode: values.writing_mode(),
+ transform,
+ font_options: draw_ctx.get_font_options(),
+ view_params: draw_ctx.get_view_params(),
+ session: draw_ctx.session().clone(),
+ };
- let chunks = self.make_chunks(node, an, cascaded, &layout_context, x, y);
+ let mut x = self.x.to_user(&params);
+ let mut y = self.y.to_user(&params);
- let mut measured_chunks = Vec::new();
- for chunk in &chunks {
- measured_chunks.push(MeasuredChunk::from_chunk(&layout_context, chunk));
- }
+ let chunks = self.make_chunks(node, acquired_nodes, cascaded, &layout_context, x, y);
- let mut positioned_chunks = Vec::new();
- for chunk in &measured_chunks {
- let chunk_x = chunk.x.unwrap_or(x);
- let chunk_y = chunk.y.unwrap_or(y);
+ let mut measured_chunks = Vec::new();
+ for chunk in &chunks {
+ measured_chunks.push(MeasuredChunk::from_chunk(&layout_context, chunk));
+ }
- let positioned =
- PositionedChunk::from_measured(&layout_context, chunk, chunk_x, chunk_y);
+ let mut positioned_chunks = Vec::new();
+ for chunk in &measured_chunks {
+ let chunk_x = chunk.x.unwrap_or(x);
+ let chunk_y = chunk.y.unwrap_or(y);
- x = positioned.next_chunk_x;
- y = positioned.next_chunk_y;
+ let positioned =
+ PositionedChunk::from_measured(&layout_context, chunk, chunk_x, chunk_y);
- positioned_chunks.push(positioned);
- }
+ x = positioned.next_chunk_x;
+ y = positioned.next_chunk_y;
+
+ positioned_chunks.push(positioned);
+ }
- let mut layout_spans = Vec::new();
- for chunk in &positioned_chunks {
- for span in &chunk.spans {
- layout_spans.push(span.layout(&layout_context, an));
- }
+ let mut layout_spans = Vec::new();
+ for chunk in &positioned_chunks {
+ for span in &chunk.spans {
+ layout_spans.push(span.layout(&layout_context, acquired_nodes));
}
+ }
- let empty_bbox = BoundingBox::new().with_transform(**transform);
+ let empty_bbox = BoundingBox::new().with_transform(transform);
- let text_bbox = layout_spans.iter().fold(empty_bbox, |mut bbox, span| {
- if let Some(ref span_bbox) = span.bbox {
- bbox.insert(span_bbox);
- }
+ let text_bbox = layout_spans.iter().fold(empty_bbox, |mut bbox, span| {
+ if let Some(ref span_bbox) = span.bbox {
+ bbox.insert(span_bbox);
+ }
- bbox
- });
+ bbox
+ });
+
+ let mut text_spans = Vec::new();
+ for span in layout_spans {
+ let stroke_paint = span.stroke_paint.to_user_space(
+ &text_bbox.rect,
+ &layout_context.view_params,
+ &span.values,
+ );
+ let fill_paint = span.fill_paint.to_user_space(
+ &text_bbox.rect,
+ &layout_context.view_params,
+ &span.values,
+ );
+
+ let text_span = TextSpan {
+ layout: span.layout,
+ gravity: span.gravity,
+ bbox: span.bbox,
+ is_visible: span.is_visible,
+ x: span.x,
+ y: span.y,
+ paint_order: span.paint_order,
+ stroke: span.stroke,
+ stroke_paint,
+ fill_paint,
+ text_rendering: span.text_rendering,
+ link_target: span.link_target,
+ };
- let mut text_spans = Vec::new();
- for span in layout_spans {
- let stroke_paint = span.stroke_paint.to_user_space(
- &text_bbox.rect,
- &layout_context.view_params,
- &span.values,
- );
- let fill_paint = span.fill_paint.to_user_space(
- &text_bbox.rect,
- &layout_context.view_params,
- &span.values,
- );
+ text_spans.push(text_span);
+ }
- let text_span = TextSpan {
- layout: span.layout,
- gravity: span.gravity,
- bbox: span.bbox,
- is_visible: span.is_visible,
- x: span.x,
- y: span.y,
- paint_order: span.paint_order,
- stroke: span.stroke,
- stroke_paint,
- fill_paint,
- text_rendering: span.text_rendering,
- link_target: span.link_target,
- };
-
- text_spans.push(text_span);
- }
+ layout::Text { spans: text_spans }
+ };
- let text_layout = layout::Text { spans: text_spans };
+ let layer = Layer {
+ kind: LayerKind::Text(Box::new(layout_text)),
+ stacking_ctx,
+ };
- dc.draw_text(&text_layout, an, clipping)
- },
- )
+ draw_ctx.draw_layer(&layer, acquired_nodes, values, clipping)
}
}