diff options
author | Michael Howell <michael@notriddle.com> | 2023-01-08 18:50:49 -0700 |
---|---|---|
committer | Marge Bot <marge-bot@gnome.org> | 2023-03-23 01:41:14 +0000 |
commit | eff355439c6b1cde7ffcc8ee6f191eaa1389dd26 (patch) | |
tree | c0f45f2bafc47bacb505f5d154205e5e07dd2c24 | |
parent | 6cb052042609a0cf0546f454f7496415912d632f (diff) | |
download | librsvg-eff355439c6b1cde7ffcc8ee6f191eaa1389dd26.tar.gz |
Layer abstraction
Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/816>
-rw-r--r-- | src/drawing_ctx.rs | 68 | ||||
-rw-r--r-- | src/layout.rs | 27 | ||||
-rw-r--r-- | src/shapes.rs | 13 | ||||
-rw-r--r-- | src/text.rs | 158 |
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(¶ms); - let mut y = self.y.to_user(¶ms); + 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(¶ms); + let mut y = self.y.to_user(¶ms); - 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) } } |