1#![forbid(unsafe_code, rust_2018_idioms)]
42
43pub use piet;
44
45use lyon_tessellation::FillRule;
46
47use piet::kurbo::{Affine, PathEl, Point, Rect, Shape, Size};
48use piet::{Error as Pierror, FixedGradient, Image as _, InterpolationMode};
49
50use piet_cosmic_text::LineProcessor;
51use tinyvec::TinyVec;
52
53use std::error::Error as StdError;
54use std::fmt;
55use std::mem;
56
57mod atlas;
58mod brush;
59mod gpu_backend;
60mod image;
61mod mask;
62mod rasterizer;
63mod resources;
64mod stroke;
65mod text;
66
67pub use self::brush::Brush;
68pub use self::gpu_backend::{BufferType, GpuContext, RepeatStrategy, Vertex};
69pub use self::image::Image;
70pub use self::text::{Text, TextLayout, TextLayoutBuilder};
71
72pub(crate) use atlas::{Atlas, GlyphData};
73pub(crate) use mask::{Mask, MaskContext};
74pub(crate) use rasterizer::{Rasterizer, TessRect};
75pub(crate) use resources::{Texture, VertexBuffer};
76
77const UV_WHITE: [f32; 2] = [0.5, 0.5];
78
79pub mod gpu_types {
81 pub use crate::gpu_backend::{AreaCapture, BufferPush, SubtextureWrite, TextureWrite};
82}
83
84pub struct Source<C: GpuContext + ?Sized> {
86 white_pixel: Texture<C>,
91
92 buffers: Buffers<C>,
94
95 text: Text,
97
98 atlas: Option<Atlas<C>>,
100
101 mask_context: MaskContext<C>,
103
104 render_states: Option<TinyVec<[RenderState<C>; 1]>>,
108
109 context: C,
111}
112
113impl<C: GpuContext + fmt::Debug + ?Sized> fmt::Debug for Source<C> {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 f.debug_struct("Source")
116 .field("context", &&self.context)
117 .finish_non_exhaustive()
118 }
119}
120
121struct Buffers<C: GpuContext + ?Sized> {
122 rasterizer: Rasterizer,
124
125 vbo: VertexBuffer<C>,
127}
128
129impl<C: GpuContext + ?Sized> Source<C> {
130 pub fn new(mut context: C, device: &C::Device, queue: &C::Queue) -> Result<Self, Pierror>
132 where
133 C: Sized,
134 {
135 const WHITE: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF];
136
137 let texture = Texture::new(
139 &mut context,
140 device,
141 InterpolationMode::NearestNeighbor,
142 RepeatStrategy::Repeat,
143 )
144 .piet_err()?;
145
146 texture.write_texture(
147 &mut context,
148 device,
149 queue,
150 (1, 1),
151 piet::ImageFormat::RgbaSeparate,
152 Some(&WHITE),
153 );
154
155 Ok(Self {
156 white_pixel: texture,
157 buffers: {
158 let vbo = VertexBuffer::new(&mut context, device).piet_err()?;
159
160 Buffers {
161 rasterizer: Rasterizer::new(),
162 vbo,
163 }
164 },
165 atlas: Some(Atlas::new(&mut context, device, queue)?),
166 mask_context: MaskContext::new(),
167 render_states: None,
168 context,
169 text: Text::new(),
170 })
171 }
172
173 pub fn context(&self) -> &C {
175 &self.context
176 }
177
178 pub fn context_mut(&mut self) -> &mut C {
180 &mut self.context
181 }
182
183 pub fn render_context<'this, 'dev, 'que>(
185 &'this mut self,
186 device: &'dev C::Device,
187 queue: &'que C::Queue,
188 width: u32,
189 height: u32,
190 ) -> RenderContext<'this, 'dev, 'que, C> {
191 RenderContext {
192 state: {
193 let mut list = self.render_states.take().unwrap_or_default();
194 list.clear();
195 list.push(RenderState::default());
196 list
197 },
198 source: self,
199 device,
200 queue,
201 size: (width, height),
202 status: Ok(()),
203 tolerance: 0.1,
204 ignore_state: false,
205 bitmap_scale: 1.0,
206 }
207 }
208
209 pub fn text(&self) -> &Text {
211 &self.text
212 }
213
214 pub fn text_mut(&mut self) -> &mut Text {
216 &mut self.text
217 }
218
219 pub fn gpu_flushed(&mut self) {
221 self.mask_context.gpu_flushed();
222 }
223}
224
225#[derive(Debug)]
227pub struct RenderContext<'src, 'dev, 'que, C: GpuContext + ?Sized> {
228 source: &'src mut Source<C>,
230
231 device: &'dev C::Device,
233
234 queue: &'que C::Queue,
236
237 size: (u32, u32),
239
240 state: TinyVec<[RenderState<C>; 1]>,
242
243 status: Result<(), Pierror>,
245
246 tolerance: f64,
248
249 bitmap_scale: f64,
251
252 ignore_state: bool,
254}
255
256#[derive(Debug)]
257struct RenderState<C: GpuContext + ?Sized> {
258 transform: Affine,
260
261 clip: ClipState<C>,
263}
264
265impl<C: GpuContext + ?Sized> Default for RenderState<C> {
266 fn default() -> Self {
267 Self {
268 transform: Affine::IDENTITY,
269 clip: ClipState::NoClip,
270 }
271 }
272}
273
274#[derive(Debug)]
276enum ClipState<C: GpuContext + ?Sized> {
277 NoClip,
279
280 SimpleRect(Rect),
282
283 Mask(Mask<C>),
285}
286
287impl<C: GpuContext + ?Sized> ClipState<C> {
288 #[inline]
290 fn into_mask(self) -> Option<Mask<C>> {
291 match self {
292 Self::Mask(mask) => Some(mask),
293 _ => None,
294 }
295 }
296
297 #[inline]
299 fn as_mut(&mut self) -> Option<&mut Mask<C>> {
300 match self {
301 Self::Mask(mask) => Some(mask),
302 _ => None,
303 }
304 }
305}
306
307impl<C: GpuContext + ?Sized> Clone for ClipState<C> {
308 #[inline]
309 fn clone(&self) -> Self {
310 match self {
311 Self::NoClip => Self::NoClip,
312 Self::SimpleRect(rect) => Self::SimpleRect(*rect),
313 Self::Mask(mask) => Self::Mask(mask.clone()),
314 }
315 }
316}
317
318impl<C: GpuContext + ?Sized> Drop for RenderContext<'_, '_, '_, C> {
319 fn drop(&mut self) {
320 match &mut self.state {
321 TinyVec::Heap(h) => self
322 .source
323 .mask_context
324 .reclaim(h.drain(..).filter_map(|s| s.clip.into_mask())),
325 TinyVec::Inline(i) => self
326 .source
327 .mask_context
328 .reclaim(i.drain(..).filter_map(|s| s.clip.into_mask())),
329 }
330
331 let mut state = mem::take(&mut self.state);
332 state.clear();
333 self.source.render_states = Some(state);
334 }
335}
336
337impl<'a, 'b, 'c, C: GpuContext + ?Sized> RenderContext<'a, 'b, 'c, C> {
338 fn temporarily_ignore_state<'this>(
340 &'this mut self,
341 ) -> TemporarilyIgnoreState<'this, 'a, 'b, 'c, C> {
342 self.ignore_state = true;
343 TemporarilyIgnoreState(self)
344 }
345
346 fn fill_rects(
348 &mut self,
349 rects: impl IntoIterator<Item = TessRect>,
350 texture: Option<&Texture<C>>,
351 ) -> Result<(), Pierror> {
352 self.source.buffers.rasterizer.fill_rects(rects);
353
354 self.push_buffers(texture)
356 }
357
358 fn fill_impl(
360 &mut self,
361 shape: impl Shape,
362 brush: &Brush<C>,
363 mode: FillRule,
364 ) -> Result<(), Pierror> {
365 self.source
366 .buffers
367 .rasterizer
368 .fill_shape(shape, mode, self.tolerance, |vert| {
369 let pos = vert.position();
370 brush.make_vertex(pos.into())
371 })?;
372
373 self.push_buffers(brush.texture(self.size).as_ref().map(|t| t.texture()))
375 }
376
377 fn stroke_impl(
378 &mut self,
379 shape: impl Shape,
380 brush: &Brush<C>,
381 width: f64,
382 style: &piet::StrokeStyle,
383 ) -> Result<(), Pierror> {
384 self.source.buffers.rasterizer.stroke_shape(
385 shape,
386 self.tolerance,
387 width,
388 style,
389 |vert| {
390 let pos = vert.position();
391 brush.make_vertex(pos.into())
392 },
393 |vert| {
394 let pos = vert.position();
395 brush.make_vertex(pos.into())
396 },
397 )?;
398
399 self.push_buffers(brush.texture(self.size).as_ref().map(|t| t.texture()))
401 }
402
403 fn push_buffers(&mut self, texture: Option<&Texture<C>>) -> Result<(), Pierror> {
405 self.source.buffers.vbo.upload(
407 &mut self.source.context,
408 self.device,
409 self.queue,
410 self.source.buffers.rasterizer.vertices(),
411 self.source.buffers.rasterizer.indices(),
412 );
413
414 let (transform, mask_texture, clip_rect, used_mask) = if self.ignore_state {
416 (
417 Affine::scale(self.bitmap_scale),
418 &self.source.white_pixel,
419 None,
420 false,
421 )
422 } else {
423 let state = self.state.last_mut().unwrap();
424
425 let (has_mask, clip_rect, mask) = match &mut state.clip {
426 ClipState::NoClip => (false, None, &self.source.white_pixel),
427 ClipState::SimpleRect(rect) => (false, Some(*rect), &self.source.white_pixel),
428 ClipState::Mask(mask) => (
429 true,
430 None,
431 self.source.mask_context.texture(
432 mask,
433 &mut self.source.context,
434 self.device,
435 self.queue,
436 ),
437 ),
438 };
439
440 (
441 Affine::scale(self.bitmap_scale) * state.transform,
442 mask,
443 clip_rect,
444 has_mask,
445 )
446 };
447
448 let texture = texture.unwrap_or(&self.source.white_pixel);
450
451 self.source
453 .context
454 .push_buffers(gpu_types::BufferPush {
455 device: self.device,
456 queue: self.queue,
457 vertex_buffer: self.source.buffers.vbo.resource(),
458 current_texture: texture.resource(),
459 mask_texture: mask_texture.resource(),
460 transform: &transform,
461 viewport_size: self.size,
462 clip: clip_rect,
463 })
464 .piet_err()?;
465
466 self.source.buffers.rasterizer.clear();
468
469 if used_mask {
471 if let Some(mask) = &mut self.state.last_mut().unwrap().clip.as_mut() {
472 self.source.mask_context.mark_used(mask);
473 }
474 }
475
476 Ok(())
477 }
478
479 fn clip_impl(&mut self, shape: impl Shape) {
480 let state = self.state.last_mut().unwrap();
481
482 if let Some(rect) = shape.as_rect() {
484 if let ClipState::NoClip = &state.clip {
485 state.clip = ClipState::SimpleRect(rect);
486 return;
487 }
488 }
489
490 let mask = match &mut state.clip {
491 ClipState::Mask(mask) => mask,
492 ClipState::SimpleRect(rect) => {
493 let mut mask = Mask::new(self.size.0, self.size.1);
495 self.source
496 .mask_context
497 .add_path(&mut mask, *rect, self.tolerance);
498 state.clip = ClipState::Mask(mask);
499 state.clip.as_mut().unwrap()
500 }
501 clip @ ClipState::NoClip => {
502 *clip = ClipState::Mask(Mask::new(self.size.0, self.size.1));
503 clip.as_mut().unwrap()
504 }
505 };
506
507 self.source
508 .mask_context
509 .add_path(mask, shape, self.tolerance);
510 }
511
512 pub fn source(&self) -> &Source<C> {
514 self.source
515 }
516
517 pub fn source_mut(&mut self) -> &mut Source<C> {
519 self.source
520 }
521
522 pub fn tolerance(&self) -> f64 {
526 self.tolerance
527 }
528
529 pub fn set_tolerance(&mut self, tolerance: f64) {
533 self.tolerance = tolerance;
534 }
535
536 pub fn bitmap_scale(&self) -> f64 {
538 self.bitmap_scale
539 }
540
541 pub fn set_bitmap_scale(&mut self, scale: f64) {
543 self.bitmap_scale = scale;
544 }
545}
546
547macro_rules! leap {
548 ($self:expr, $e:expr) => {{
549 match $e {
550 Ok(v) => v,
551 Err(e) => {
552 $self.status = Err(Pierror::BackendError(e.into()));
553 return;
554 }
555 }
556 }};
557 ($self:expr, $e:expr, $err:expr) => {{
558 match $e {
559 Ok(v) => v,
560 Err(e) => {
561 let err = $err;
562 $self.status = Err(err.into());
563 return;
564 }
565 }
566 }};
567}
568
569impl<C: GpuContext + ?Sized> piet::RenderContext for RenderContext<'_, '_, '_, C> {
570 type Brush = Brush<C>;
571 type Text = Text;
572 type TextLayout = TextLayout;
573 type Image = Image<C>;
574
575 fn status(&mut self) -> Result<(), Pierror> {
576 mem::replace(&mut self.status, Ok(()))
577 }
578
579 fn solid_brush(&mut self, color: piet::Color) -> Self::Brush {
580 Brush::solid(color)
581 }
582
583 fn gradient(&mut self, gradient: impl Into<FixedGradient>) -> Result<Self::Brush, Pierror> {
584 match gradient.into() {
585 FixedGradient::Linear(linear) => {
586 Brush::linear_gradient(&mut self.source.context, self.device, self.queue, linear)
587 }
588 FixedGradient::Radial(radial) => {
589 Brush::radial_gradient(&mut self.source.context, self.device, self.queue, radial)
590 }
591 }
592 }
593
594 fn clear(&mut self, region: impl Into<Option<Rect>>, mut color: piet::Color) {
595 let region = region.into();
596
597 let clamp = |x: f64| {
599 if x < 0.0 {
600 0.0
601 } else if x > 1.0 {
602 1.0
603 } else {
604 x
605 }
606 };
607 let (r, g, b, a) = color.as_rgba();
608 let r = clamp(r * a);
609 let g = clamp(g * a);
610 let b = clamp(b * a);
611 color = piet::Color::rgba(r, g, b, 1.0);
612
613 if region.is_none() {
615 self.source.context.clear(self.device, self.queue, color);
616 return;
617 }
618
619 let ignore_state = self.temporarily_ignore_state();
621
622 let result = ignore_state.0.fill_rects(
624 {
625 let uv_white = Point::new(UV_WHITE[0] as f64, UV_WHITE[1] as f64);
626 [TessRect {
627 pos: region.unwrap_or_else(|| {
628 Rect::from_origin_size(
629 (0.0, 0.0),
630 (ignore_state.0.size.0 as f64, ignore_state.0.size.1 as f64),
631 )
632 }),
633 uv: Rect::from_points(uv_white, uv_white),
634 color,
635 }]
636 },
637 None,
638 );
639
640 leap!(ignore_state.0, result);
641 }
642
643 fn stroke(&mut self, shape: impl Shape, brush: &impl piet::IntoBrush<Self>, width: f64) {
644 let brush = brush.make_brush(self, || shape.bounding_box());
645 if let Err(e) =
646 self.stroke_impl(shape, brush.as_ref(), width, &piet::StrokeStyle::default())
647 {
648 self.status = Err(e);
649 }
650 }
651
652 fn stroke_styled(
653 &mut self,
654 shape: impl Shape,
655 brush: &impl piet::IntoBrush<Self>,
656 width: f64,
657 style: &piet::StrokeStyle,
658 ) {
659 let brush = brush.make_brush(self, || shape.bounding_box());
660 if let Err(e) = self.stroke_impl(shape, brush.as_ref(), width, style) {
661 self.status = Err(e);
662 }
663 }
664
665 fn fill(&mut self, shape: impl Shape, brush: &impl piet::IntoBrush<Self>) {
666 let brush = brush.make_brush(self, || shape.bounding_box());
667 if let Err(e) = self.fill_impl(shape, brush.as_ref(), FillRule::NonZero) {
668 self.status = Err(e);
669 }
670 }
671
672 fn fill_even_odd(&mut self, shape: impl Shape, brush: &impl piet::IntoBrush<Self>) {
673 let brush = brush.make_brush(self, || shape.bounding_box());
674 if let Err(e) = self.fill_impl(shape, brush.as_ref(), FillRule::EvenOdd) {
675 self.status = Err(e);
676 }
677 }
678
679 fn clip(&mut self, shape: impl Shape) {
680 if (self.bitmap_scale - 1.0).abs() > 0.001 {
682 let mut path = shape.into_path(self.tolerance);
683 path.apply_affine(Affine::scale(self.bitmap_scale));
684 self.clip_impl(path);
685 } else {
686 self.clip_impl(shape);
687 }
688 }
689
690 fn text(&mut self) -> &mut Self::Text {
691 &mut self.source.text
692 }
693
694 fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<Point>) {
695 struct RestoreAtlas<'a, 'b, 'c, 'd, G: GpuContext + ?Sized> {
696 context: &'a mut RenderContext<'b, 'c, 'd, G>,
697 atlas: Option<Atlas<G>>,
698 }
699
700 impl<G: GpuContext + ?Sized> Drop for RestoreAtlas<'_, '_, '_, '_, G> {
701 fn drop(&mut self) {
702 self.context.source.atlas = Some(self.atlas.take().unwrap());
703 }
704 }
705
706 let pos = pos.into();
707 let mut restore = RestoreAtlas {
708 atlas: self.source.atlas.take(),
709 context: self,
710 };
711
712 let texture = restore.atlas.as_ref().unwrap().texture().clone();
714
715 let text = restore.context.text().clone();
716 let device = restore.context.device;
717 let queue = restore.context.queue;
718 let mut line_state = LineProcessor::new();
719 let rects = layout
720 .buffer()
721 .layout_runs()
722 .flat_map(|run| {
723 run.glyphs
725 .iter()
726 .map(move |glyph| (glyph, run.line_y as f64))
727 })
728 .filter_map({
729 let atlas = restore.atlas.as_mut().unwrap();
730 |(glyph, line_y)| {
731 let GlyphData {
733 uv_rect,
734 offset,
735 size,
736 } = match text.with_font_system_mut(|fs| {
737 atlas.uv_rect(
738 &mut restore.context.source.context,
739 device,
740 queue,
741 glyph,
742 fs,
743 )
744 }) {
745 Some(Ok(rect)) => rect,
746 Some(Err(e)) => {
747 tracing::trace!("failed to get uv rect: {}", e);
748 return None;
749 }
750 None => {
751 tracing::trace!("font system not loaded yet");
753 return None;
754 }
755 };
756
757 let physical = glyph.physical((0.0, 0.0), 1.0);
758
759 let pos_rect = Rect::from_origin_size(
761 (
762 physical.x as f64 + pos.x + offset.x,
763 physical.y as f64 + line_y + pos.y - offset.y,
764 ),
765 size,
766 );
767
768 let color = glyph.color_opt.unwrap_or({
769 let piet_color = piet::util::DEFAULT_TEXT_COLOR;
770 let (r, g, b, a) = piet_color.as_rgba8();
771 cosmic_text::Color::rgba(r, g, b, a)
772 });
773 let piet_color =
774 glyph
775 .color_opt
776 .map_or(piet::util::DEFAULT_TEXT_COLOR, |color| {
777 let [r, g, b, a] = [color.r(), color.g(), color.b(), color.a()];
778 piet::Color::rgba8(r, g, b, a)
779 });
780
781 line_state.handle_glyph(glyph, line_y as f32, color);
783
784 Some(TessRect {
785 pos: pos_rect,
786 uv: uv_rect,
787 color: piet_color,
788 })
789 }
790 })
791 .collect::<Vec<_>>();
792 let result = restore.context.fill_rects(rects, Some(&texture));
793
794 drop(restore);
795
796 let lines_result = {
797 let lines = line_state.lines();
798 if lines.is_empty() {
799 Ok(())
800 } else {
801 self.fill_rects(
802 lines.into_iter().map(|line| {
803 let mut rect = line.into_rect();
804 rect.x0 += pos.x;
805 rect.y0 += pos.y;
806 rect.x1 += pos.x;
807 rect.y1 += pos.y;
808 TessRect {
809 pos: rect,
810 uv: Rect::new(0.5, 0.5, 0.5, 0.5),
811 color: line.color,
812 }
813 }),
814 None,
815 )
816 }
817 };
818
819 leap!(self, result);
820 leap!(self, lines_result);
821 }
822
823 fn save(&mut self) -> Result<(), Pierror> {
824 let last = self.state.last().unwrap();
825 self.state.push(RenderState {
826 transform: last.transform,
827 clip: last.clip.clone(),
828 });
829 Ok(())
830 }
831
832 fn restore(&mut self) -> Result<(), Pierror> {
833 if self.state.len() <= 1 {
834 return Err(Pierror::StackUnbalance);
835 }
836
837 let mut state = self.state.pop().unwrap();
838 self.source.mask_context.reclaim(
839 mem::replace(&mut state.clip, ClipState::NoClip)
840 .into_mask()
841 .into_iter(),
842 );
843
844 Ok(())
845 }
846
847 fn finish(&mut self) -> Result<(), Pierror> {
848 self.source
849 .context
850 .flush()
851 .map_err(|x| Pierror::BackendError(x.into()))
852 }
853
854 fn transform(&mut self, transform: Affine) {
855 let slot = &mut self.state.last_mut().unwrap().transform;
856 *slot *= transform;
857 }
858
859 fn make_image(
860 &mut self,
861 width: usize,
862 height: usize,
863 buf: &[u8],
864 format: piet::ImageFormat,
865 ) -> Result<Self::Image, Pierror> {
866 let tex = Texture::new(
867 &mut self.source.context,
868 self.device,
869 InterpolationMode::Bilinear,
870 RepeatStrategy::Color(piet::Color::TRANSPARENT),
871 )
872 .piet_err()?;
873
874 tex.write_texture(
875 &mut self.source.context,
876 self.device,
877 self.queue,
878 (width as u32, height as u32),
879 format,
880 Some(buf),
881 );
882
883 Ok(Image::new(tex, Size::new(width as f64, height as f64)))
884 }
885
886 fn draw_image(
887 &mut self,
888 image: &Self::Image,
889 dst_rect: impl Into<Rect>,
890 interp: piet::InterpolationMode,
891 ) {
892 self.draw_image_area(image, Rect::ZERO.with_size(image.size()), dst_rect, interp)
893 }
894
895 fn draw_image_area(
896 &mut self,
897 image: &Self::Image,
898 src_rect: impl Into<Rect>,
899 dst_rect: impl Into<Rect>,
900 interp: piet::InterpolationMode,
901 ) {
902 let pos_rect = dst_rect.into();
904 let uv_rect = {
905 let scale_x = 1.0 / image.size().width;
906 let scale_y = 1.0 / image.size().height;
907
908 let src_rect = src_rect.into();
909 Rect::new(
910 src_rect.x0 * scale_x,
911 src_rect.y0 * scale_y,
912 src_rect.x1 * scale_x,
913 src_rect.y1 * scale_y,
914 )
915 };
916
917 image
919 .texture()
920 .set_interpolation(&mut self.source.context, self.device, interp);
921
922 if let Err(e) = self.fill_rects(
924 [TessRect {
925 pos: pos_rect,
926 uv: uv_rect,
927 color: piet::Color::WHITE,
928 }],
929 Some(image.texture()),
930 ) {
931 self.status = Err(e);
932 }
933 }
934
935 fn capture_image_area(&mut self, src_rect: impl Into<Rect>) -> Result<Self::Image, Pierror> {
936 let src_rect = src_rect.into();
937 let src_size = src_rect.size();
938 let src_bitmap_size = Size::new(
939 src_size.width * self.bitmap_scale,
940 src_size.height * self.bitmap_scale,
941 );
942
943 let image = {
945 let texture = Texture::new(
946 &mut self.source.context,
947 self.device,
948 InterpolationMode::Bilinear,
949 RepeatStrategy::Repeat,
950 )
951 .piet_err()?;
952
953 Image::new(texture, src_bitmap_size)
954 };
955
956 let offset = (src_rect.x0 as u32, src_rect.y0 as u32);
958 let size = (src_size.width as u32, src_size.height as u32);
959 self.source
960 .context
961 .capture_area(gpu_backend::AreaCapture {
962 device: self.device,
963 queue: self.queue,
964 texture: image.texture().resource(),
965 offset,
966 size,
967 bitmap_scale: self.bitmap_scale,
968 })
969 .piet_err()?;
970
971 Ok(image)
972 }
973
974 fn blurred_rect(
975 &mut self,
976 input_rect: Rect,
977 blur_radius: f64,
978 brush: &impl piet::IntoBrush<Self>,
979 ) {
980 let size = piet::util::size_for_blurred_rect(input_rect, blur_radius);
981 let width = size.width as u32;
982 let height = size.height as u32;
983 if width == 0 || height == 0 {
984 return;
985 }
986
987 let (mask, rect_exp) = {
989 let mut mask = tiny_skia::Mask::new(width, height).unwrap();
990
991 let rect_exp = piet::util::compute_blurred_rect(
992 input_rect,
993 blur_radius,
994 width.try_into().unwrap(),
995 mask.data_mut(),
996 );
997
998 (mask, rect_exp)
999 };
1000
1001 let mut image = tiny_skia::Pixmap::new(width, height)
1003 .expect("Pixmap width/height should be valid clipmask width/height");
1004 let shader = match brush.make_brush(self, || input_rect).to_shader() {
1005 Some(shader) => shader,
1006 None => {
1007 self.status = Err(Pierror::BackendError("Failed to create shader".into()));
1008 return;
1009 }
1010 };
1011 image.fill(tiny_skia::Color::TRANSPARENT);
1012 image.fill_rect(
1013 tiny_skia::Rect::from_xywh(0., 0., width as f32, height as f32).unwrap(),
1014 &tiny_skia::Paint {
1015 shader,
1016 ..Default::default()
1017 },
1018 tiny_skia::Transform::identity(),
1019 Some(&mask),
1020 );
1021
1022 let image = leap!(
1024 self,
1025 self.make_image(
1026 width as usize,
1027 height as usize,
1028 image.data(),
1029 piet::ImageFormat::RgbaSeparate
1030 )
1031 );
1032 self.draw_image(&image, rect_exp, piet::InterpolationMode::Bilinear);
1033 }
1034
1035 fn current_transform(&self) -> Affine {
1036 self.state.last().unwrap().transform
1037 }
1038}
1039
1040struct TemporarilyIgnoreState<'this, 'a, 'b, 'c, C: GpuContext + ?Sized>(
1041 &'this mut RenderContext<'a, 'b, 'c, C>,
1042);
1043
1044impl<C: GpuContext + ?Sized> Drop for TemporarilyIgnoreState<'_, '_, '_, '_, C> {
1045 fn drop(&mut self) {
1046 self.0.ignore_state = false;
1047 }
1048}
1049
1050trait ResultExt<T, E: StdError + 'static> {
1051 fn piet_err(self) -> Result<T, Pierror>;
1052}
1053
1054impl<T, E: StdError + 'static> ResultExt<T, E> for Result<T, E> {
1055 fn piet_err(self) -> Result<T, Pierror> {
1056 self.map_err(|e| Pierror::BackendError(Box::new(LibraryError(e))))
1057 }
1058}
1059
1060struct LibraryError<E>(E);
1061
1062impl<E: fmt::Debug> fmt::Debug for LibraryError<E> {
1063 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1064 fmt::Debug::fmt(&self.0, f)
1065 }
1066}
1067
1068impl<E: fmt::Display> fmt::Display for LibraryError<E> {
1069 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1070 fmt::Display::fmt(&self.0, f)
1071 }
1072}
1073
1074impl<E: StdError> StdError for LibraryError<E> {}
1075
1076fn shape_to_skia_path(builder: &mut tiny_skia::PathBuilder, shape: impl Shape, tolerance: f64) {
1078 shape.path_elements(tolerance).for_each(|el| match el {
1079 PathEl::MoveTo(pt) => builder.move_to(pt.x as f32, pt.y as f32),
1080 PathEl::LineTo(pt) => builder.line_to(pt.x as f32, pt.y as f32),
1081 PathEl::QuadTo(p1, p2) => {
1082 builder.quad_to(p1.x as f32, p1.y as f32, p2.x as f32, p2.y as f32)
1083 }
1084 PathEl::CurveTo(p1, p2, p3) => builder.cubic_to(
1085 p1.x as f32,
1086 p1.y as f32,
1087 p2.x as f32,
1088 p2.y as f32,
1089 p3.x as f32,
1090 p3.y as f32,
1091 ),
1092 PathEl::ClosePath => builder.close(),
1093 })
1094}