From 57bc614cf472c080800afff6df74b354375be4b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20d=27Herbais=20de=20Thun?= Date: Wed, 4 Oct 2023 10:58:17 +0200 Subject: [PATCH] Gradient Part 2a - Fix sharp gradients in SVG (#2307) --- crates/typst/src/export/svg.rs | 24 ++++++++++++++++++++---- crates/typst/src/geom/gradient.rs | 4 ++-- tests/ref/visualize/gradient-sharp.png | Bin 16748 -> 45753 bytes tests/typ/visualize/gradient-sharp.typ | 11 ++++++++++- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/crates/typst/src/export/svg.rs b/crates/typst/src/export/svg.rs index 2b99c3ffe..03e121810 100644 --- a/crates/typst/src/export/svg.rs +++ b/crates/typst/src/export/svg.rs @@ -666,16 +666,27 @@ impl SVGRenderer { self.xml.write_attribute("y2", &y2); for window in linear.stops.windows(2) { - let (_, start_t) = window[0]; - let (_, end_t) = window[1]; + let (start_c, start_t) = window[0]; + let (end_c, end_t) = window[1]; + + self.xml.start_element("stop"); + self.xml + .write_attribute_fmt("offset", format_args!("{start_t:?}")); + self.xml.write_attribute("stop-color", &start_c.to_hex()); + self.xml.end_element(); // Generate (256 / len) stops between the two stops. // This is a workaround for a bug in many readers: // They tend to just ignore the color space of the gradient. // The goal is to have smooth gradients but not to balloon the file size // too much if there are already a lot of stops as in most presets. - let len = (256 / linear.stops.len() as u32).max(1); - for i in 0..len { + let len = if gradient.anti_alias() { + (256 / linear.stops.len() as u32).max(2) + } else { + 2 + }; + + for i in 1..(len - 1) { let t0 = i as f64 / (len - 1) as f64; let t = start_t + (end_t - start_t) * t0; let c = gradient.sample(RatioOrAngle::Ratio(t)); @@ -685,6 +696,11 @@ impl SVGRenderer { self.xml.write_attribute("stop-color", &c.to_hex()); self.xml.end_element(); } + + self.xml.start_element("stop"); + self.xml.write_attribute_fmt("offset", format_args!("{end_t:?}")); + self.xml.write_attribute("stop-color", &end_c.to_hex()); + self.xml.end_element() } self.xml.end_element(); diff --git a/crates/typst/src/geom/gradient.rs b/crates/typst/src/geom/gradient.rs index 8f920f3b0..387636267 100644 --- a/crates/typst/src/geom/gradient.rs +++ b/crates/typst/src/geom/gradient.rs @@ -445,7 +445,7 @@ impl Gradient { angle: grad.angle, space: grad.space, relative: grad.relative, - anti_alias: true, + anti_alias: grad.anti_alias, })), }) } @@ -762,7 +762,7 @@ fn sample_stops(stops: &[(Color, Ratio)], mixing_space: ColorSpace, t: f64) -> C let hue_0 = if hue_0 < hue_1 { hue_0 + 360.0 } else { hue_0 }; let hue_1 = if hue_1 < hue_0 { hue_1 + 360.0 } else { hue_1 }; - let hue = (hue_0 * (1.0 - t as f32) + hue_1 * t as f32).rem_euclid(360.0); + let hue = hue_0 * (1.0 - t as f32) + hue_1 * t as f32; if mixing_space == ColorSpace::Hsl { let [_, saturation, lightness, alpha] = out.to_hsl().to_vec4(); diff --git a/tests/ref/visualize/gradient-sharp.png b/tests/ref/visualize/gradient-sharp.png index 8e50f597e2f41b4a0e29c4ebade3239686a3c703..30e6fb6662ec91cef32d794461b7dfebbe9e0986 100644 GIT binary patch literal 45753 zcmeI5eM}p59L8HjM@T?nnPdodFnmb}Fs8h0l@hh1G()Cl&=^W_F4jb_c9gCJGLASL zn^5Xl;Eo0-gl!6%V%eGJT6x(7MGTUHpRx~HU1l0&Bm?;f{mky#RUc2CSAwbNOO@`ppYvR z`^+1MEc%ckn<){L-y5+*P%fV-Y5YfDJ;~js(9Tv3r&2`W5j!%%EW*$}X*PdxPa^|= z{QV>lADL@C754mZ*~;zoNfBjaxmmZi5GuNmSgX5ATvQXYM}4Yl`*f_qi`3-1eXk%z& zXk%z&Xk%z&Xk%z&Xk%z&Xk)CoW59rEiV5}@_89gU_89gU_89gU_89gU_89gU_89gU z_89gU_86&DL~xNeMphfabfoZ6IY3W)oZqw1Bm-quU<)=Rxb{g2R+ z`b6QaZ^k3z*g^e`b3Ee|t6F(~?ih!T;kfJ-YPzt!HfG!Ca72}d_0tpIQ>0&2ePr$C z(gz)L(*aIU-#3|5CRZsNPLET`w8T+%AP_6m9dnRWeVTS}nB=?iF$X1(XlP21vY1iJkYOshlo!(t@{>$+;LVMT|bHURr zwB1wOZ6=z`0Y7QaZ@)vGpm^)8IkY_3B~<6-4B5qsl@S%8byiQ5&K>NszM5&6Fj&@0 zXGg+G&BA&XEt+(+cU2n4_ygMGgtq0OJbSjdQ*GXrW0|))gqxcO`4=mXKV>qIxuVHj z;a9oB$(k~^tB)M}IJ*1`>3N&=^SQdq%TyBSYuvo-VJ&zXf~$U2%858;n6YIvn?A|0 znTYr4$H~{s%G-n26ZI~ewNjVRQ{tUnQoLl$2<>ZROJBxVZ{Cfq785)ozzfnX-M}-Q?v1l2~Va@;;9%tBu?lO=d=uJLle`bWXYS(`VySh08wg z>-`UBvWbZ-&U|=);`#mp7lF$Lmkll(+--2T!9xlUDQFvL8%SbEVmK){DL5$@a2Rl` z+hE-W%Y9hx!zwXWi7{NjZ~?;w%%m`r!YC7?OiaKr0Y`8G!3pGTkhekJ1~Dzfw26^j}|mhzL3&=x8pWxq!k83NPqMp(llU9qM&xWulddRwl~4DD$EN zjt)4`0YC?UI051WKqUZ`08|3L4fr;|A_0p8nignUfUp6=26i9VePH(i`3U4ANTDEw z0x%1}EV#tr5(8%qoVA+xvsaM;XAPV+aMr+C17{7KHE`CzSp#PcoHcOPz*z%l4V*P_ s*1%Zt7jcjaY@qxoUmEL(ei{%-#RjL^p4Q_R&o^YUB20FS9^A^-pY delta 51 zcmdn_l<7?q;{a2R4IIS}*fZ_%mStFTO3V)^#o+1c=d#Wz Gp$PzKEDgi} diff --git a/tests/typ/visualize/gradient-sharp.typ b/tests/typ/visualize/gradient-sharp.typ index e19df58fb..424beb8b7 100644 --- a/tests/typ/visualize/gradient-sharp.typ +++ b/tests/typ/visualize/gradient-sharp.typ @@ -1,4 +1,13 @@ // Test sharp gradients. --- -#square(size: 100pt, fill: gradient.linear(..color.map.rainbow).sharp(10)) +#square( + size: 100pt, + fill: gradient.linear(..color.map.rainbow, space: color.hsl).sharp(10), +) + +--- +#square( + size: 100pt, + fill: gradient.linear(..color.map.rainbow, space: color.hsl).sharp(10, smoothness: 40%), +)