From 4ea250c0a0668889dddcb638edaa9f255e8f1568 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Tue, 21 May 2024 15:40:14 +0100 Subject: [PATCH] Implement Skrifa + Tiny-Skia example --- Cargo.lock | 45 +++++++++ Cargo.toml | 1 + examples/tiny-skia.rs | 210 ++++++++++++++++++++++++++++++++++++++++++ output.png | Bin 19009 -> 22159 bytes src/style/brush.rs | 12 +-- 5 files changed, 257 insertions(+), 11 deletions(-) create mode 100644 examples/tiny-skia.rs diff --git a/Cargo.lock b/Cargo.lock index 745be6cd..a48769a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "arrayvec" version = "0.7.4" @@ -409,6 +415,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d642685b028806386b2b6e75685faadd3eb65a85fff7df711ce18446a422da" +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + [[package]] name = "memmap2" version = "0.9.4" @@ -452,6 +464,7 @@ dependencies = [ "peniko", "skrifa", "swash", + "tiny-skia", ] [[package]] @@ -560,6 +573,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + [[package]] name = "swash" version = "0.1.15" @@ -613,6 +632,32 @@ dependencies = [ "syn", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinystr" version = "0.7.5" diff --git a/Cargo.toml b/Cargo.toml index cf1eac08..e927986b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ fontique = { workspace = true } [dev-dependencies] image = { version = "0.25.1", default-features = false, features = ["png"] } +tiny-skia = "0.11.4" [workspace.dependencies] fontique = { version = "0.1.0", default-features = false, path = "fontique" } diff --git a/examples/tiny-skia.rs b/examples/tiny-skia.rs new file mode 100644 index 00000000..d4e1d940 --- /dev/null +++ b/examples/tiny-skia.rs @@ -0,0 +1,210 @@ +// Copyright 2024 the Parley Authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! A simple example that lays out some text using Parley, extracts outlines using Skrifa and +//! then paints those outlines using Tiny-Skia. + +use parley::layout::{Alignment, GlyphRun, Layout}; +use parley::style::{FontStack, FontWeight, StyleProperty}; +use parley::{FontContext, LayoutContext}; +use peniko::Color as PenikoColor; +use skrifa::instance::{LocationRef, Size}; +use skrifa::outline::{DrawSettings, OutlinePen}; +use skrifa::raw::FontRef as ReadFontsRef; +use skrifa::{GlyphId, MetadataProvider, OutlineGlyph}; +use tiny_skia::{ + Color as TinySkiaColor, FillRule, Paint, PathBuilder, Pixmap, PixmapMut, Rect, Transform, +}; + +fn main() { + // The text we are going to style and lay out + let text = String::from( + "Some text here. Let's make it a bit longer so that line wrapping kicks in 😊. And also some اللغة العربية arabic text.", + ); + + // The display scale for HiDPI rendering + let display_scale = 1.0; + + // The width for line wrapping + let max_advance = Some(200.0 * display_scale); + + // Colours for rendering + let foreground_color = PenikoColor::rgb8(0, 0, 0); + let background_color = PenikoColor::rgb8(255, 255, 255); + + // Padding around the output image + let padding = 20; + + // Create a FontContext, LayoutContext + // + // These are both intended to be constructed rarely (perhaps even once per app (or once per thread)) + // and provide caches and scratch space to avoid allocations + let mut font_cx = FontContext::default(); + let mut layout_cx = LayoutContext::new(); + + // Create a RangedBuilder + let mut builder = layout_cx.ranged_builder(&mut font_cx, &text, display_scale); + + // Set default text colour styles (set foreground text color) + let brush_style = StyleProperty::Brush(foreground_color); + builder.push_default(&brush_style); + + // Set default font family + let font_stack = FontStack::Source("system-ui"); + let font_stack_style = StyleProperty::FontStack(font_stack); + builder.push_default(&font_stack_style); + builder.push_default(&StyleProperty::LineHeight(1.3)); + builder.push_default(&StyleProperty::FontSize(16.0)); + + // Set the first 4 characters to bold + let bold = FontWeight::new(600.0); + let bold_style = StyleProperty::FontWeight(bold); + builder.push(&bold_style, 0..4); + + // Build the builder into a Layout + let mut layout: Layout = builder.build(); + + // Perform layout (including bidi resolution and shaping) with start alignment + layout.break_all_lines(max_advance, Alignment::Start); + let width = layout.width().ceil() as u32; + let height = layout.height().ceil() as u32; + let padded_width = width + padding * 2; + let padded_height = height + padding * 2; + + // Create TinySkia Pixmap + let mut img = Pixmap::new(padded_width, padded_height).unwrap(); + + // Fill background color + let mut bg_paint = Paint::default(); + bg_paint.set_color(to_tiny_skia(background_color)); + img.fill_rect( + Rect::from_xywh(0., 0., padded_width as f32, padded_height as f32).unwrap(), + &bg_paint, + Transform::identity(), + None, + ); + + let mut pen = TinySkiaPen::new(img.as_mut()); + + // Iterate over laid out lines + for line in layout.lines() { + // Iterate over GlyphRun's within each line + for glyph_run in line.glyph_runs() { + render_glyph_run(&glyph_run, &mut pen, padding); + } + } + + // Write image to PNG file + img.save_png("output.png").unwrap(); +} + +fn to_tiny_skia(color: PenikoColor) -> TinySkiaColor { + TinySkiaColor::from_rgba8(color.r, color.g, color.b, color.a) +} + +fn render_glyph_run(glyph_run: &GlyphRun, pen: &mut TinySkiaPen<'_>, padding: u32) { + // Resolve properties of the GlyphRun + let mut run_x = glyph_run.offset(); + let run_y = glyph_run.baseline(); + let style = glyph_run.style(); + let color = style.brush; + + // Get the "Run" from the "GlyphRun" + let run = glyph_run.run(); + + // Resolve properties of the Run + let font = run.font(); + let font_size = run.font_size(); + + // Get glyph outlines using Skrifa. This can be cached in production code. + let font_collection_ref = font.data.as_ref(); + let font_ref = ReadFontsRef::from_index(font_collection_ref, font.index).unwrap(); + let outlines = font_ref.outline_glyphs(); + + // Iterates over the glyphs in the GlyphRun + for glyph in glyph_run.glyphs() { + let glyph_x = run_x + glyph.x + padding as f32; + let glyph_y = run_y - glyph.y + padding as f32; + run_x += glyph.advance; + + let glyph_id = GlyphId::from(glyph.id); + let glyph_outline = outlines.get(glyph_id).unwrap(); + + pen.set_origin(glyph_x, glyph_y); + pen.set_color(to_tiny_skia(color)); + pen.draw_glyph(&glyph_outline, font_size); + } +} + +struct TinySkiaPen<'a> { + pixmap: PixmapMut<'a>, + x: f32, + y: f32, + paint: Paint<'static>, + open_path: PathBuilder, +} + +impl TinySkiaPen<'_> { + fn new<'a>(pixmap: PixmapMut<'a>) -> TinySkiaPen<'a> { + TinySkiaPen { + pixmap, + x: 0.0, + y: 0.0, + paint: Paint::default(), + open_path: PathBuilder::new(), + } + } + + fn set_origin(&mut self, x: f32, y: f32) { + self.x = x; + self.y = y; + } + + fn set_color(&mut self, color: TinySkiaColor) { + self.paint.set_color(color); + } + + fn draw_glyph(&mut self, glyph: &OutlineGlyph<'_>, size: f32) { + let settings = DrawSettings::unhinted(Size::new(size), LocationRef::default()); + glyph.draw(settings, self).unwrap(); + } +} + +impl OutlinePen for TinySkiaPen<'_> { + fn move_to(&mut self, x: f32, y: f32) { + self.open_path.move_to(self.x + x, self.y - y); + } + + fn line_to(&mut self, x: f32, y: f32) { + self.open_path.line_to(self.x + x, self.y - y); + } + + fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) { + self.open_path + .quad_to(self.x + cx0, self.y - cy0, self.x + x, self.y - y) + } + + fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) { + self.open_path.cubic_to( + self.x + cx0, + self.y - cy0, + self.x + cx1, + self.y - cy1, + self.x + x, + self.y - y, + ) + } + + fn close(&mut self) { + let new_builder = PathBuilder::new(); + let builder = core::mem::replace(&mut self.open_path, new_builder); + let path = builder.finish().unwrap(); + self.pixmap.fill_path( + &path, + &self.paint, + FillRule::EvenOdd, + Transform::identity(), + None, + ); + } +} diff --git a/output.png b/output.png index d6575fc41902ebc91e16bbe12fd79ec072f3432b..21a2e36df10b871ed2b5d7a2ad6c9d97e2b98ad9 100644 GIT binary patch literal 22159 zcmdU1ZFrRBm40J15fRfx1(lD`H7@PCHB~G45g4pi%+i|L7P(p^!P_Pzcq97?Us{AXEuMRS25EBvBJSBp*z|WRm&VbKlR*^S%>;+H1Ez z>UAlY%slV&an612bD#6P%YS;`;?UG^>6rSK4I4N2dM^BA z+_V`_918!-zHOB!H*5@zzjIsd)M+!iU4!1tmM?4w|73M`?xCsEf;mh6VTSN$oVnTN zy*bdG6~1Nfs^r#9$4+j_?^v3=KRmnQ*n;b;s;a8Hrnr6(zC3WCJ#bmlf`d!>TgM-! z=hXby?6l9Wu0A}cW?1;>lX-dmQkQ>3=E>JGpZRR>DsOvtog-MMz8o(*XgiV>?#}X# z%khqDSa51n^~Y0cuD}yYZkW;hs;&1`|AzA3e+{jFyZi9;g7acy_j9TzHtwH&E*d3@Pva zRr#La_TG)Iwa(B9XG?{nufpGcw%!%2uRd_9`Z8Ddbl09auCsHz?VX3Rf`NAXUn@q#-1dn=Z`>Nf29Wy-LGpcHM@j_-R^&JM^VnO$I9nja|Ka_M*eH zJ3ny-KM73N-&mU6=EGDup8V;mbyRplr#&rCWVSxhv&qx@z0AI?S*4CuM~}|f-dE)+ zT@^aMDxq;}R^QgR9u7PLJAQCe#j*GA8hz$l_EQ!16?OL0buEi0_P%Oci}S%F8`=*~ z&-vnNcYXra_F7i&YX$asHQf)Cbv`@0V_o8e-zD05b{aS?IcRsIc?Nm*);uGBQ{95^ zWv=FuawoRtZmG@fd)eb3WoR3Cg_DsvXBK;UH+h;d!Gd}6U}tSk_-u|BYaC$o@^Ei? z<>M!YYHQ1_KYH|woKQ;+AKB)+!{xohpy8!nl3sXt_M%}Nb1={C!FL{Co;cz7ooPKg zlUnTDy;E}LGfVq|I1%&dlVlWE{0hr?kCyb9?jC{ufsHucD#$ zO~8!T+fS~~KT%@;4O4ys`AB5=Gc4JJJwbdlvXsAhfbEOncxHy>#NVsagBp@?+Rv~+M2n7%U9#` zukaDNMNr1tzQSj3_a)2}|K(tCoJtQL4tF&;SZeUY)LM67zZ-k~>rz+fl#3>QLd{pfp-P;iS%h?6%&vn6T zTQ+V#xiNp9#Z2j^aIm*M8aM*Of4HsIv4vllv%O^wj|DSX%THsm`RnwY^5-SlJC+vo ze&oEYD22|*X^G8L3+&wjv)?tn=Fs%cqp~zHnKOswLG9W+VyAnoJ$Q`aD8teF5zv?@ zyJy94KMohp>-|WcKm(UUwC$NLaPI{G18fR?d5-b&_wrhC=OmeU{>F+0*8@gJ6mf$W zZf{-KBgS$)M_O`k^IH>7z18!jJ2bb@hzRHGeZ$!-hOkNh zNL*$NAMi%Wg73nwt|{U!5Xt0p4&?rR_mrBCr*r}bN80Q!@wYg7J*85y9-cEoZCda| zMk^2-o~E7ot*WXIxRv7toA{B~tt1Q}aBLyy<_bIze&}uM9lh%d5blY`u!BMgg15*9 zx7WrKFR`~vvke5C5k<5kSKOP~q04E_ba$Xn?uietGZC$@=}kF1k&6 z;Ny(o6$-QXz}sc?fW+S`fdoT#E^`tQytVK^ z>&y|#4?!P3kqEIEmPv^DYjW_fFl-o}cAB0)xqJT&bG$~aBQ}_}o<&5IQ425IR_P&E zxk=oB=1O;hh0~fpl*u-<7F(<;EAie;Ph z2Gj<5FmmJsVT~nQY6m|SJvD$bQI*H10_vI`y~CE3!GbddlPqAh1tu@l#!(CElj|Zi z%0AYTtxQ8sT8^W^9USTGyxO@~5iD64ENuDZWA#(kD#+zPJjO{Te2pjqA|*7!2K_lj zb4$a&0ie)OaAqBhX|S<1`FDz6Ouj#NNUFa;#4BR5;U~>(Da%si9|sL68bh=F#mL&J-e)) z17k8Y^`w;L;4l|7BEb7!bo#FzeH?_S2b4z+CG?DfVxc&GV1>bB2Z1Vd`wEkix}toF zq%QS$DSGiOm2@<2CxT(eq}1-8rS54Li0hapoRp7OZkV~+1bKL^1~#t6y-Ec$^8^T} zb8}6mvSr8)kV2o#iTiA%VRN{oQ?Ae)b03WLEz!MHpi=l0Uy&M>zEbwMs>P z$^t6!QNm`MWHp@}{11im{UXH%Zf z3Db__)0<%hMh}_U$&K5eX>FyW!7S+PFtGDJ*BcWt>S@6@CVOuzJTz_5m2Nka4%5U7 z9s9UV)2UMsb@&kcSVW0H^$fL4-Zdlc{m>dRk&EC zhmI5VHs+f9Kd8Qx0c5hHYqA&gflg@dXoueA{`qC%8fBx{8G>~*g4G5?!Yr>TVtCc& z0jG~drLUM&^_Vp(7|EwgTc5;9tu9V`O7D=0Kk*%iG#2kKbIP$u78F_*O-T&$5K>AV zn$lK(Jqpy)e-N*j`1|@0X=M{9CMd9jWvR3kQ{cfOO!R;N3){PGe)EMoAm_Re$Y3xa zz+Y`JpGuSl5`z_yQH>T@oG;vgMoiq+WXnuMDupA}TmgI>QuGbhhPVsf`o$50d!T?D_VznGT=6#KSXxWACpz=6>+2ESE7iJ{YxA}X4X>U z>8mN2N1cRG1etDm?}l<_tBh|1-BrD>uVU7vS*<0Nx8sTR!m~T3O8x}~yGqj8CghTm z!hIq6ln$qchN+$7e0?UUkS+;IC7UV%bq4X#1Y0Y#Pv2GvL+@tB3)h3 z$V3x3DBqWKOJ^^N0u2;^$t|NPm(h4<(t>Znd24wL4M?UCXz1rUQu;9^McV~VZjjv<*D6VF?5HkuUwifCj-jaibkb8lvg;$Yk=L`(KInV6xX zyHWxm;}8Vb^25k#jQoR3NAE^t@nV67PHL)LeN_cx(yPa815ww6z<6%d9q-Y#lpF;d zl`Z#=G6japAf!p~gj$$`0mNWdN^5rirwzi40Tyu&OQDld zbyUKDX0YzlR@%duj{F*mrndXg;BSi(_gC(7l)hAt!8tK|T=hS~ts{WtFF~*}_&B>b zcNB~qn*>~87GQj?z2I-)IGMk&d?x%zbV#Zr*ks^r>OL8p`i|{W`CAk*JSV~U3ZOED zwjzwJWfXH=_4n1W1LJ#P#s(2`_~+mUk|@GP9Dl*8aK1eFjb*2q1tV{!TP}+RzPbgQ z>J$a%>^JvjES41G9w6qB9o*z4FWowSNyUPV70s|YYy{Ko=uo+RaAHu4#QK2a2?@P# z$_YU9&9$X7*(3V84v|ItOqe+4*J>)q+vtj!Ex*lNM!1A6SDv(ul$>}(4HPoPDh8iH zbmdu+14R+ff}TnUWg=S|E@!&HumPj0bec%g;`w*e$h{2~AT@{^fInpQn|qUrWiwKg ztO;RZzWh`)3o{|Z_TMn$9jXrMmu8?6$WXGfH4`pa2MCNarC&Gp#b7u|tB9V8NNJrU z3N~1wJO90#0UBi8UMf7;LlSo;z0fj7-tpwKG`h;8QF-~upC6{=p1LnK^YZ;(Q82P0 zi2GWso_EgUBHTTa!Of8J6)RQ-h_igu1siQ9GOQz`p*9||PUM~E46l@X8ngr> zdofb`ghu!;V;PJyzJpN*RSI!pCCGuvC|TJ6bz25$pv6w}%?N&+f%Db2(xin=Gwob# zev64L%QqnX?i^>VF-siGsp&vWv&pz&NLrSO>p@P+m54POl_G%qqzNVOOk|bU}_C5wGofasW z7S@qgK~3nlf=%WI%BB!*F_IG1qWD+bdX^pq`K^D})0f^1TEf_fSffv;38c8^_^Qw& z=b0pz$5R9R#;rCp{4}$KwZ+QgTXfLZ+^GejfjJdI6ghTOETf5$j>vBFPXaYgZev-Z zfN=b2(f!B|QMV)+(ox!cz+&@s{Qjr`mvY{m4mQbLv_6K2C`92w?ItvWGN*4rxsUON z)D8GRQPf5Mhn~#rlst8Uq!Vd2lZU~P7$&e^l^&LIfD&5lRno0@02<5^l@4Fcy9a?J z*r`R)z$~?yNKDa%8HE!Hl%N#oQB+Zaf)GNM6rps|6GL~#Pz_TRJ)VGmKIq(q!}F7-rV0S=y7z2 z0sK*wdQ?4$X->P?LX*D$C1&M^s9tN=Z1b67Jzb7*(*mLQy>#o&$%{Uo94Wig9msi6 zVVdiGm%)eHFC7l06OAk@MWWPFOo58wm@+fqwy~z+N9vhcB4$RVMQ$1ZN~`=Xm=}QT z8tbX#S`uL~4h=IVh*H~BWm5$@L`5V5suqIrQEWlwctb1X_DPJ_$oWz6MwyE85KLcf z#zEQwjn~GiDf2JD5NB+rg{%gkTE|FeCC+Bj?)7H2cW0U|Oid)s za%vTz+lX+SISm4?M@C{YfM`i*t(DG4^FpMkl`=0?cw;q)dqH8z3W1bQ5lHck#mLsl z-)QpMwwUk34v?{;4FQrfXQDlQt~a0$br;xaC-eb)9#+O81nE#zhg{Zr_5q=6ls6;f z1g0bfgngp%T4_Zj-XK^JC`fdW@{t1}6jKsI(*ZdlKvn36NrP4O1w>TJoR)A$s~^yw zDXl}WWo`gQq-q7yxnX|><|=|?1Ub!;y^==gYsfLoR5M=X7KGqB@e+fbH`EkCeB+qQ zXz)y+v_pN))JV)G4`I)YvqT4=nkw*6eu0s&j;nZ1e`rZyWs4Oylz1Nf^2m)!1Cz!h zC1ypqHadu=l-v+USEcLIjiVVZs@@t2BiRtnmTgn~1?+_dW9cI{lQZRHR z>H7Ay=LcLal@V9e%lIN{l`;}7jpw|B{2jd+BdpglJ2MUEc(4oOkNjGAqis5Jp0-?-DtxV{m zAK**INPop6q@gkEQt5Q8`6&nFVOba=!CYlmGrB#--_{b@I~*<#y9`Vx1Dh5smd+Mw znH+4gZO^;ozUIfOa)vQp0zNLZBivR9l$RDobxe zft@r&b)saQVJJpNIC0l&!?DcB9SJh!%xXXXx)pJw$f~fo{{#vS6F4&;io}+QMAlwft;D8 zbVS6@a~k!elLCHeYCnNFLG-n1MTzZ?Y%p-Fp!oiyPIQ6LR-?{FFhvg<3!CPo$Vvnl zJ0q&$NXwRZ88c90iE3q$xJ81bN@!mri-ptCiFzD$8&tc=Mh%@#X;IoNYN9gFwwoeM z3WR~@1-w8p51qD^9tI>-Z!8^oG-)01}l*V~^!2jDg^d@O{Vkk(KKTe?q;%ZD+3l7mdJR@L^mS zwN|YPTH1L)^k6Q;Js2PT2ZwDc6?`%jzOs73D|9X*1U|p}&kyf?8;!s!_eU%&X@rF#J23P34%P<_YKdB7>BALl!$)p+;z>I)B~i)7fWzKGoBz zdO#?#g7)!?I4t(tfCN#Lq=i}XJsnhJ2CAc;@CWN)ZbRLU%K?_8Rgkm8?Rt`yA%&Fy zhAIv%8c!oBrIAx@fHRR-!Im{MraGV)n`$7UGlS7c0fE4U4Wq>{FY|hJ=f1R}A${Z| zW3t|rmOO#VdPK(8c0RB{vG$IP!t7g>j$~Pgg;0`j!R{(O;`&SgS;zsK#2k=Au?>OR zJago+%XP&mj_3{ggFP4+s95zTeRE-hQ3hqs?D`PRd}cEwvlvirI&CnellnO-nZAYk zh-n{U#sJZPw$N`mi2QLCfGQ2ef{~} zgU`S%js5{lf1=WZTu>=-(DAfrtaLm!E1}Wk_+;B!DGAz|DKSS{W@*GYDeg%tB_V|o zY|xBCw}lhX+L;Wos|(*PQ@v#8H&BvJ0tXy0&4Mk{Q2^7;THHg46h>Oppp3?1M^7;k z7ec=^0roA7@vnP^rtZUeL?ksr0h=dOO;_ItAP7Z^42ktH4!R4k<)H1FIW^=0I;fTi zGFNMV_a{lp5}wKR5bB^PvEC03MqlAF>CiA#*2K)>Ymntxw2o={*kF$drC1Jz4@Y1? zUmp?}fQ=0sQzMeNz`)-;T_S&3EBVg&vG$5i7 za}fB0RT~+*=);qfn3t4NT7fJ77`Veaq81q~Smgx702DygkoA2dlVBV}(@ehI9N9`_xglgPI`*(k8VM_~Ru)xJ z+GSjzc8lNzwO?IhV&LF(>0}7Yf_H2eV&&ORP6%2z)6%|a_8nMy(EDp=;Y%WsS8qYV zI)Vk)B&^KZ;MCtD=*u`nWFBnzZoipn{K~>~bEvP>mcc9gx;SaWw;)x~>IEi?!88 z&4H-|@PpiwX*w+=(tnEENxHEOI+KdW(tC|a$cjAX%?p?7^bB?7SgQH@VqK75$pj1aP6t!7eI$m36vz?8brL6u7#IFea74+$(;Is#WLfi0`26pz$2 zka(BWzm^cyP{(~czw@7|V9=J(4;HOETxGfVQ0ZZ22`St_YRS$Fo`+@)4DHB=nakrk zf$D5yNdU($T@Xmc2&$?F$J%LlfGa#d6)=q_k{#nh7Bqa_ZAdnzLtj#txm%SUs>e`l zNMy0u(M%iDBZWsUse0)+L!P+~<#OYII~IivR>LVAio!Ff2)8pC&v;grK3t6(mOEFf z)W_tJ=xO=5>*;JW!`6)nZ9XbCXc;CFdc8<_xZlV;E!vsla7#;}u6Z7QB)Ou)*k#+Il>3b*&WbsNh zX_Zi)%9FM$*zK6r0b)U$4l8wGYWR=;zxVjVvz3`be5a3qSBI_#2a%m0{fL|v_H|N3 zV4RJNg>6XRk<-SO_;CMO*3gg>`Dj)X`yT;xz@~Ri;=o&Hlx-Gbn*@ zjp}IsTjDD(E$WW%I36l-p3HtR!!WPjzkKY(%ROsS{X?Tr_k!auOx|&BN)^J)IdYJr zlE}N3SdwSI+32KmSbrS^=a}wH$5U*Wie5AiPl3YzA_EWXP(<{L*cTW6Byu?#q{85n zHO)+`-~xo4Ms8w4!|2mpK_LFpHM}j3B@oi4X!>Q``|h~#l@_|6x{u}sbECtoJU2|1 zhA+1CZG^8AFL2`0Gj}f9(da&nhNkn}vyZC9MN#GvmC0KdBdnq;Pqi%$-y<9eK5naz zb+djOQ2n~uo25}vu261^h4I^_Zr$^X-0mue^%rH z9W*QgeJalvt)=>!j-}wMaa}1_c%1o;fXD?K=7_22=AFq@jXRIM;rkp^RF^=6u10IS zBY#0AND$EPT;SzpkOD#-+c|iz7eWfvF&3fmP-Velm;}ZO1=B03hgtUXr6ZZ z8bs9j5UQ}}OzVKnLsYdDs8AU8Iczzd!*X^?Z>J*;N-}G8xY)+Sf=fXezyRcY4Amy* zt56*~q7btqlBlAax&eutI8$NWfFf~?L#-j})s+!#uJU`HPRvF2T=bn$ieYb@id4$K zz=$|tzhg_YaRf?=OuoO9^3aGPrZYfyhM~5xgX$x?CR`ss$E-~qOlxekr)W0ICJckg z3fUg2ZQ#zPR*Ea&&@|WN+j+*4pN_J&6p^}trIC!#I~ZeOtW;3A(H0V=eN#za=!yM` z+0wF^rywyF(-@6jKD`*Lp%Dv{k5gKIv83Q?Qgvh;35`~Yg1hVpA1Z5%>xkjBk-Rvn zhf0);?=Xof@7bDgCJ>LD-t2KSGw--YouZ-BXfWR9Q0c+0BYcLg!vv_;LW;$kBq+tY zXbsS{$r06!UP~4f;~6?Fj9d^xt2VI&%!^n8yzOU776gbWq5G`>OWPphfm1cKlRQ9@ zW&I4(k9oTb6gu5o6G^S`X*9!WuT!Hi{eglykaVodAviO$LPMlbm#5VoAPDq0e_Y35 z-)Dy9l7j0(O1O9{4vSk{0}4UBrwpr<7M#dMU{G5+pr(qkSEnpG0mB}kgBQJ$9+wss zRTaQnb?jPPdDNwqM5TWzqiJa8jsMqI^?rxsMnMv=S!oVV>8fe6>ym|9CeaP&5J5N1 z^OL&tL=0)=DBlAD5|6qa&IB}_FRP(QaNd&#B;j^AV}P9JB|DHOK|L=paV!b z$tGp*RmO;EDBL^eLj6TMVk>3gH3JYB{)nv!mP_qySCscFjVJ|T8NyNQl+1Fqv@5dfRkboO+L8prh}@@vd2lC7hcr`bvm&K( zZV+9zPTOT$L@e^=^`5kPhzA zw|2>O54_kWDwpukDewk31j2O8E4X6c_@Ft!krq}Ibt8Ff3#Gy>vQBXeHUTm8O9@2w7?DeJ?e#+XCR<=0jYeSD%*_X!TT&ro?;odbH?avMC~(N)X893W@$ksDxeQ z+!kI>VVftkLNRNyQFO(SwdkhOT$6#G3tEw~nz|aH)Je--9K2OZ%Uej?bdNfdT=FJM zb|Nq6ir%~_tEWMrBdnnvStyf4o+;Zy4w`I<9ad(5UP4WS!7g6jn^ZHLW>1OsU3L#K zIz^_6aE0U!rwo{im2|NWruNz|)nlaipnCrY>g^V2AK{P45GmQAO9Jy+nNsFtGZnDO z+A&KK%$X7#*=LLQTyZ8;J^@&1aErbg$34j`T*VtD{o6_n4hoq0H zlph%&*)_tj^?HJO(Nno_ktVxtGVlgW5715iNb$f=cy+~yxko=h^~9i~(OIF|@kNt5 z97qE%YI3Y%W22HU5t%EUSsq^awS+i@?Xi6*BwjX|^(b4tR^rJDKqJDX6mfYnuJ?%S zOha)pJmTSd%*;T3hLPD~kkrK*Bw#c=j9y1x>oP=0Y-ZIL+>&<@**1WO5m%X~qRCF% zYNV4wLmsi7jSlj{(Wga33O8U5bbe`2JP_zi4&MmvhpYvy*6huuxTymhSuJqN(05tT zT02}y7*iptvVe9Sfr$PrkNO9EW9fNbz1lPRt&d&~{nNUMU&@PSjr1SgclYKW&Rp`p D__Iig literal 19009 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 diff --git a/src/style/brush.rs b/src/style/brush.rs index c0961ee0..ad671f06 100644 --- a/src/style/brush.rs +++ b/src/style/brush.rs @@ -4,14 +4,4 @@ /// Trait for types that represent the color of glyphs or decorations. pub trait Brush: Clone + PartialEq + Default + core::fmt::Debug {} -/// Empty brush. -impl Brush for () {} - -/// Brush for a 4-byte color value. -impl Brush for [u8; 4] {} - -/// Brush for a 3-byte color value. -impl Brush for [u8; 3] {} - -impl Brush for peniko::Brush {} -impl Brush for peniko::Color {} +impl Brush for T {}