From 45377f25ecbd33b56ec5fcd1335f20b13488a93d Mon Sep 17 00:00:00 2001 From: Eric Biedert Date: Mon, 28 Oct 2024 15:31:00 +0100 Subject: [PATCH] Fix clipping with outset (#5295) Co-authored-by: Laurenz --- crates/typst-layout/src/flow/block.rs | 6 ++---- crates/typst-layout/src/inline/box.rs | 3 +-- crates/typst-layout/src/shapes.rs | 5 +++++ crates/typst-library/src/visualize/path.rs | 20 ++++++++++++++++++++ tests/ref/box-clip-outset.png | Bin 0 -> 1442 bytes tests/suite/layout/container.typ | 13 +++++++++++++ 6 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 tests/ref/box-clip-outset.png diff --git a/crates/typst-layout/src/flow/block.rs b/crates/typst-layout/src/flow/block.rs index 1dd988120..48ca1fba7 100644 --- a/crates/typst-layout/src/flow/block.rs +++ b/crates/typst-layout/src/flow/block.rs @@ -84,8 +84,7 @@ pub fn layout_single_block( // Clip the contents, if requested. if elem.clip(styles) { - let size = frame.size() + outset.relative_to(frame.size()).sum_by_axis(); - frame.clip(clip_rect(size, &radius, &stroke)); + frame.clip(clip_rect(frame.size(), &radius, &stroke, &outset)); } // Add fill and/or stroke. @@ -231,8 +230,7 @@ pub fn layout_multi_block( // Clip the contents, if requested. if clip { - let size = frame.size() + outset.relative_to(frame.size()).sum_by_axis(); - frame.clip(clip_rect(size, &radius, &stroke)); + frame.clip(clip_rect(frame.size(), &radius, &stroke, &outset)); } // Add fill and/or stroke. diff --git a/crates/typst-layout/src/inline/box.rs b/crates/typst-layout/src/inline/box.rs index 30572e4e6..62054bead 100644 --- a/crates/typst-layout/src/inline/box.rs +++ b/crates/typst-layout/src/inline/box.rs @@ -61,8 +61,7 @@ pub fn layout_box( // Clip the contents, if requested. if elem.clip(styles) { - let size = frame.size() + outset.relative_to(frame.size()).sum_by_axis(); - frame.clip(clip_rect(size, &radius, &stroke)); + frame.clip(clip_rect(frame.size(), &radius, &stroke, &outset)); } // Add fill and/or stroke. diff --git a/crates/typst-layout/src/shapes.rs b/crates/typst-layout/src/shapes.rs index 31f8c42be..81be12190 100644 --- a/crates/typst-layout/src/shapes.rs +++ b/crates/typst-layout/src/shapes.rs @@ -412,7 +412,11 @@ pub fn clip_rect( size: Size, radius: &Corners>, stroke: &Sides>, + outset: &Sides>, ) -> Path { + let outset = outset.relative_to(size); + let size = size + outset.sum_by_axis(); + let stroke_widths = stroke .as_ref() .map(|s| s.as_ref().map_or(Abs::zero(), |s| s.thickness / 2.0)); @@ -441,6 +445,7 @@ pub fn clip_rect( } } path.close_path(); + path.translate(Point::new(-outset.left, -outset.top)); path } diff --git a/crates/typst-library/src/visualize/path.rs b/crates/typst-library/src/visualize/path.rs index 76fd0df0f..f592a7124 100644 --- a/crates/typst-library/src/visualize/path.rs +++ b/crates/typst-library/src/visualize/path.rs @@ -1,4 +1,5 @@ use kurbo::ParamCurveExtrema; +use typst_utils::Numeric; use self::PathVertex::{AllControlPoints, MirroredControlPoint, Vertex}; use crate::diag::{bail, SourceResult}; @@ -228,6 +229,25 @@ impl Path { self.0.push(PathItem::ClosePath); } + /// Translate all points in this path by the given offset. + pub fn translate(&mut self, offset: Point) { + if offset.is_zero() { + return; + } + for item in self.0.iter_mut() { + match item { + PathItem::MoveTo(p) => *p += offset, + PathItem::LineTo(p) => *p += offset, + PathItem::CubicTo(p1, p2, p3) => { + *p1 += offset; + *p2 += offset; + *p3 += offset; + } + PathItem::ClosePath => (), + } + } + } + /// Computes the size of bounding box of this path. pub fn bbox_size(&self) -> Size { let mut min_x = Abs::inf(); diff --git a/tests/ref/box-clip-outset.png b/tests/ref/box-clip-outset.png new file mode 100644 index 0000000000000000000000000000000000000000..21538e85fcd68ce5c533ba9f05b4330400c89ca1 GIT binary patch literal 1442 zcmV;T1zq}yP)^1*VYM3V&f%xg_!hxU>G=fIZ2>RbcZ#J9l zWxZbiDSDnq6ZlmwVcYi8$FA!(V4Ws`EH}56)oO*H_kw=vrRy3)5jBp|)rPyUo!h=W z=qQp{Wof?3aV*o3h5Nhy7ppt%$yE|xEWU34BIv!Km&@hv)97|5y7X;6xu4!Rv(a>H z-i^9*tLHk?ACFUaZhW5`GgH(Ae9*4TRK~31rouNRY1;uo?||NJjK_xufymoEb!H7l zx~w-C4OVVGLd$I34)sA>7^)SkO_^dr7fJ>sG!f9rV}(k?O3s>0&_wSw(T?M!!vU2q zs{%3BrH%mFT%jvbeN`A~O+%?cBB^p2HWgxX7doW+GNXxCbv(*t&zhA4PhbdoFX%|< zK*VF^_z9JHTZ#rjJao-qO{Umsu&PKF<7Z?(R(OAM?thgG2Vf=JSNNe?>q#UX4-l9B zahtCI96|4Z_B=0nen=)xvB+T+iTnKf)sikVt(VYY2J=v}IWWNaL{}7Cp*eye`L&TPy~NOdX^|B1`3>mZ5y= zwMJS)gQR&~)g%srU+vo6OH*MS2k<|ki|&G;F2o3vh=jz%)NsB;nQzPaHal&)m79uh zn@hLmOgHCt+}hmBahuNRUUg2}o15FR7p264$e`{rK@hwU^hW5w1KxCTy6Nusy?EgH zajrh+@Z<1%9_Jh}G0=`}r2;{xVI%7O_2UabQ$Vl9qT9(Omwwn|QY_oGZlio7;*Ge8 zt<>_jPsz==pKKJL*Mpl;??%|OoqYZC>(-LPxVGZrV{~~xFTUd1Oa?zBf&fhcy&76c zxQ%wD(5{w*TxOqH8Fmx%lOtY>dOhTRzq%N(>*GF4$f0*nj#`z2@4`OYq|#^C$tB(G z>^hfKR>Yfni~4(=!h`r#e%K{uZR42(TJJPWwka|^>UlO z@9dz*7F-UWULxku38M~;{m8b19%eHE`rl4i{;r&yob2uG9UdN%UoHZjo1U_$BnkmX zH7Y~R{cOtmdv_P0;mi=A0UDqI8lV9hE;#};TttTeG(ZD11fT&L0?+^r&=7zIXb3<9 wG(bZD8lWKn4bT7$0ce1R05m`YG~~bM5BFfZ9?w3c3;+NC07*qoM6N<$f+b+MssI20 literal 0 HcmV?d00001 diff --git a/tests/suite/layout/container.typ b/tests/suite/layout/container.typ index e13679675..799300f0d 100644 --- a/tests/suite/layout/container.typ +++ b/tests/suite/layout/container.typ @@ -251,6 +251,19 @@ First! image("/assets/images/rhino.png", width: 30pt) ) +--- box-clip-outset --- +// Test clipping with `outset`. +#set page(height: 60pt) + +#box( + outset: 5pt, + stroke: 2pt + black, + width: 20pt, + height: 20pt, + clip: true, + image("/assets/images/rhino.png", width: 30pt) +) + --- container-layoutable-child --- // Test box/block sizing with directly layoutable child. //