From f2028f687500873246e69a41ec0f7c0ab99a6da8 Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Tue, 31 Oct 2023 19:49:36 +0800 Subject: [PATCH] fix(exporter::svg): partially disable incr rendering (#387) * fix(exporter): partially disable incr rendering + on elements that contains gradient fill --- exporter/svg/src/backend/mod.rs | 14 +++++++---- exporter/svg/src/frontend/context.rs | 35 +++++++++++++++++++++++++++- exporter/svg/src/frontend/mod.rs | 4 ++++ exporter/svg/src/utils.rs | 15 ++++++++++++ 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/exporter/svg/src/backend/mod.rs b/exporter/svg/src/backend/mod.rs index 6a04ea13..af105540 100644 --- a/exporter/svg/src/backend/mod.rs +++ b/exporter/svg/src/backend/mod.rs @@ -20,7 +20,7 @@ use typst_ts_core::{ mod escape; use escape::{PcDataEscapes, TextContentDataEscapes}; -use crate::utils::ToCssExt; +use crate::{frontend::HasGradient, utils::ToCssExt}; pub trait BuildClipPath { fn build_clip_path(&mut self, path: &ir::PathItem) -> Fingerprint; @@ -586,8 +586,10 @@ impl< } /// See [`FlatGroupContext`]. -impl<'m, C: FlatIncrRenderVm<'m, Resultant = Arc, Group = SvgTextBuilder>> - FlatIncrGroupContext for SvgTextBuilder +impl< + 'm, + C: FlatIncrRenderVm<'m, Resultant = Arc, Group = SvgTextBuilder> + HasGradient, + > FlatIncrGroupContext for SvgTextBuilder { fn render_diff_item_ref_at( &mut self, @@ -597,7 +599,8 @@ impl<'m, C: FlatIncrRenderVm<'m, Resultant = Arc, Group = SvgTextBu item: &Fingerprint, prev_item: &Fingerprint, ) { - let content = if item == prev_item { + let has_gradient = ctx.has_gradient(item); + let content = if item == prev_item && !has_gradient { // todo: update transform vec![] } else { @@ -612,6 +615,9 @@ impl<'m, C: FlatIncrRenderVm<'m, Resultant = Arc, Group = SvgTextBu }; attributes.push(("data-tid", item.as_svg_id("p"))); attributes.push(("data-reuse-from", prev_item.as_svg_id("p"))); + if has_gradient { + attributes.push(("data-bad-equality", "1".to_owned())); + } self.content.push(SvgText::Content(Arc::new(SvgTextNode { attributes, diff --git a/exporter/svg/src/frontend/context.rs b/exporter/svg/src/frontend/context.rs index 97bfee0d..8b94aa34 100644 --- a/exporter/svg/src/frontend/context.rs +++ b/exporter/svg/src/frontend/context.rs @@ -8,7 +8,7 @@ use typst_ts_core::{ flat_vm::{FlatGroupContext, FlatIncrRenderVm, FlatRenderVm}, ir::{ self, BuildGlyph, FontIndice, FontRef, GlyphHashStablizer, GlyphIndice, GlyphItem, - GlyphPackBuilder, GlyphRef, ImmutStr, PathItem, Scalar, StyleNs, + GlyphPackBuilder, GlyphRef, ImmutStr, PathItem, PathStyle, Scalar, StyleNs, }, vm::GroupContext, vm::{RenderState, RenderVm}, @@ -21,9 +21,12 @@ use crate::{ BuildClipPath, BuildFillStyleClass, DynExportFeature, NotifyPaint, SvgText, SvgTextBuilder, SvgTextNode, }, + utils::MemorizeFree, ExportFeature, GlyphProvider, SvgGlyphBuilder, }; +use super::HasGradient; + /// Maps the style name to the style definition. /// See [`StyleNs`]. pub(crate) type StyleDefMap = HashMap<(StyleNs, ImmutStr), String>; @@ -146,6 +149,36 @@ impl<'m, 't, Feat: ExportFeature> BuildClipPath for RenderContext<'m, 't, Feat> } } +#[comemo::memoize] +fn has_gradient<'m, 't, Feat: ExportFeature>( + ctx: &MemorizeFree>, + x: &Fingerprint, +) -> bool { + let Some(item) = ctx.0.get_item(x) else { + // overestimated + return true; + }; + + use FlatSvgItem::*; + match item { + Gradient(..) => true, + Image(..) | Link(..) | None => false, + Item(t) => has_gradient(ctx, &t.1), + Group(g, ..) => g.0.iter().any(|(_, x)| has_gradient(ctx, x)), + Path(p) => p.styles.iter().any(|s| match s { + PathStyle::Fill(color) | PathStyle::Stroke(color) => color.starts_with('@'), + _ => false, + }), + Text(p) => p.shape.fill.starts_with('@'), + } +} + +impl<'m, 't, Feat: ExportFeature> HasGradient for RenderContext<'m, 't, Feat> { + fn has_gradient(&self, a: &Fingerprint) -> bool { + has_gradient(&MemorizeFree(self), a) + } +} + impl<'m, 't, Feat: ExportFeature> NotifyPaint for RenderContext<'m, 't, Feat> { fn notify_paint(&mut self, url_ref: ImmutStr) -> (u8, Fingerprint, Option) { if let Some(f) = self.gradients.get(&url_ref) { diff --git a/exporter/svg/src/frontend/mod.rs b/exporter/svg/src/frontend/mod.rs index 3763f6b1..66e68460 100644 --- a/exporter/svg/src/frontend/mod.rs +++ b/exporter/svg/src/frontend/mod.rs @@ -752,3 +752,7 @@ impl std::fmt::Display for RatioRepr { write!(f, "{:.3}%", self.0 * 100.0) } } + +pub trait HasGradient { + fn has_gradient(&self, f: &Fingerprint) -> bool; +} diff --git a/exporter/svg/src/utils.rs b/exporter/svg/src/utils.rs index 059ac6d1..0f104664 100644 --- a/exporter/svg/src/utils.rs +++ b/exporter/svg/src/utils.rs @@ -60,3 +60,18 @@ impl ToCssExt for ir::Transform { ) } } + +#[derive(Clone, Copy)] +pub(crate) struct MemorizeFree<'a, T>(pub &'a T); + +impl<'a, T> std::hash::Hash for MemorizeFree<'a, T> { + fn hash(&self, _state: &mut H) {} +} + +impl<'a, T> std::cmp::PartialEq for MemorizeFree<'a, T> { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl<'a, T> std::cmp::Eq for MemorizeFree<'a, T> {}