piet_hardware/
lib.rs

1// SPDX-License-Identifier: LGPL-3.0-or-later OR MPL-2.0
2// This file is a part of `piet-hardware`.
3//
4// `piet-hardware` is free software: you can redistribute it and/or modify it under the
5// terms of either:
6//
7// * GNU Lesser General Public License as published by the Free Software Foundation, either
8//   version 3 of the License, or (at your option) any later version.
9// * Mozilla Public License as published by the Mozilla Foundation, version 2.
10//
11// `piet-hardware` is distributed in the hope that it will be useful, but WITHOUT ANY
12// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13// PURPOSE. See the GNU Lesser General Public License or the Mozilla Public License for more
14// details.
15//
16// You should have received a copy of the GNU Lesser General Public License and the Mozilla
17// Public License along with `piet-hardware`. If not, see <https://www.gnu.org/licenses/>.
18
19//! An adaptor for [`piet`] that allows it to take advantage of GPU acceleration.
20//!
21//! This crate provides common types, traits and functionality that should be useful for
22//! implementations of the [`piet`] drawing framework for hardware-accelerated backends
23//! like OpenGL, Vulkan and WGPU. It handles things like rasterization, atlas packing and
24//! memory management, while leaving the actual implementation of the GPU commands to the
25//! backend.
26//!
27//! To use, first implement the [`GpuContext`] trait on a type of your choice that represents
28//! an active GPU context. Wrap this type in the [`Source`] type, and then use that to
29//! create a [`RenderContext`]. From here, you can pass that type to your rendering code. It
30//! conforms to the [`piet`] API, so you can use it as a drop-in replacement for any [`piet`]
31//! backend, including [`piet-common`].
32//!
33//! Note that this crate generally uses thread-unsafe primitives. This is because UI management is
34//! usually pinned to one thread anyways, and it's a bad idea to do drawing outside of that thread.
35//!
36//! ## Implementation
37//!
38//! This crate works first and foremost by converting drawing operations to a series of
39//! triangles.
40
41#![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
79/// Structures that are useful for implementing the `GpuContext` type.
80pub mod gpu_types {
81    pub use crate::gpu_backend::{AreaCapture, BufferPush, SubtextureWrite, TextureWrite};
82}
83
84/// The source of the GPU renderer.
85pub struct Source<C: GpuContext + ?Sized> {
86    /// A texture that consists of an endless repeating pattern of a single white pixel.
87    ///
88    /// This is used for solid-color fills. It is also used as the mask for when a
89    /// clipping mask is not defined.
90    white_pixel: Texture<C>,
91
92    /// The buffers used by the GPU renderer.
93    buffers: Buffers<C>,
94
95    /// The text API.
96    text: Text,
97
98    /// The font atlas.
99    atlas: Option<Atlas<C>>,
100
101    /// The mask rendering context.
102    mask_context: MaskContext<C>,
103
104    /// The cached list of render states.
105    ///
106    /// This is always empty, but it keeps the memory around.
107    render_states: Option<TinyVec<[RenderState<C>; 1]>>,
108
109    /// The context to use for the GPU renderer.
110    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    /// The rasterizer for the GPU renderer.
123    rasterizer: Rasterizer,
124
125    /// The VBO for vertices.
126    vbo: VertexBuffer<C>,
127}
128
129impl<C: GpuContext + ?Sized> Source<C> {
130    /// Create a new source from a context.
131    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        // Setup a white pixel texture.
138        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    /// Get a reference to the context.
174    pub fn context(&self) -> &C {
175        &self.context
176    }
177
178    /// Get a mutable reference to the context.
179    pub fn context_mut(&mut self) -> &mut C {
180        &mut self.context
181    }
182
183    /// Create a new rendering context.
184    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    /// Get a reference to the text backend.
210    pub fn text(&self) -> &Text {
211        &self.text
212    }
213
214    /// Get a mutable reference to the text backend.
215    pub fn text_mut(&mut self) -> &mut Text {
216        &mut self.text
217    }
218
219    /// Indicate that we've flushed the queue and all of the GPU resources can be overwritten.
220    pub fn gpu_flushed(&mut self) {
221        self.mask_context.gpu_flushed();
222    }
223}
224
225/// The whole point of this crate.
226#[derive(Debug)]
227pub struct RenderContext<'src, 'dev, 'que, C: GpuContext + ?Sized> {
228    /// The source of the GPU renderer.
229    source: &'src mut Source<C>,
230
231    /// The device that we are rendering to.
232    device: &'dev C::Device,
233
234    /// The queue that we are rendering to.
235    queue: &'que C::Queue,
236
237    /// The width and height of the target.
238    size: (u32, u32),
239
240    /// The current state of the renderer.
241    state: TinyVec<[RenderState<C>; 1]>,
242
243    /// The result to use for `status`.
244    status: Result<(), Pierror>,
245
246    /// Tolerance for tesselation.
247    tolerance: f64,
248
249    /// Scale to apply for bitmaps.
250    bitmap_scale: f64,
251
252    /// Flag to ignore the current state.
253    ignore_state: bool,
254}
255
256#[derive(Debug)]
257struct RenderState<C: GpuContext + ?Sized> {
258    /// The current transform in pixel space.
259    transform: Affine,
260
261    /// The current clip.
262    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/// Current state for clipping.
275#[derive(Debug)]
276enum ClipState<C: GpuContext + ?Sized> {
277    /// There is no clip at all.
278    NoClip,
279
280    /// There is a simple rectangle to use for clips.
281    SimpleRect(Rect),
282
283    /// There is a more complicated texture mask.
284    Mask(Mask<C>),
285}
286
287impl<C: GpuContext + ?Sized> ClipState<C> {
288    /// Convert into an `Option<Mask>`
289    #[inline]
290    fn into_mask(self) -> Option<Mask<C>> {
291        match self {
292            Self::Mask(mask) => Some(mask),
293            _ => None,
294        }
295    }
296
297    /// Convert into an `Option<&mut Mask>`.
298    #[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    /// Temporarily ignore the transform and the clip.
339    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    /// Fill in a rectangle.
347    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        // Push the buffers to the GPU.
355        self.push_buffers(texture)
356    }
357
358    /// Fill in the provided shape.
359    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        // Push the incoming buffers.
374        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        // Push the incoming buffers.
400        self.push_buffers(brush.texture(self.size).as_ref().map(|t| t.texture()))
401    }
402
403    /// Push the values currently in the renderer to the GPU.
404    fn push_buffers(&mut self, texture: Option<&Texture<C>>) -> Result<(), Pierror> {
405        // Upload the vertex and index buffers.
406        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        // Decide which mask and transform to use.
415        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        // Decide the texture to use.
449        let texture = texture.unwrap_or(&self.source.white_pixel);
450
451        // Draw!
452        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        // Clear the original buffers.
467        self.source.buffers.rasterizer.clear();
468
469        // Mark the mask as used so we don't overwrite it.
470        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 this shape is just a rectangle, use a simple scissor rect instead.
483        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                // Create a clip mask with the existing rectangle
494                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    /// Get the source of this render context.
513    pub fn source(&self) -> &Source<C> {
514        self.source
515    }
516
517    /// Get a mutable reference to the source of this render context.
518    pub fn source_mut(&mut self) -> &mut Source<C> {
519        self.source
520    }
521
522    /// Get the current tolerance for tesselation.
523    ///
524    /// This is used to convert curves into line segments.
525    pub fn tolerance(&self) -> f64 {
526        self.tolerance
527    }
528
529    /// Set the current tolerance for tesselation.
530    ///
531    /// This is used to convert curves into line segments.
532    pub fn set_tolerance(&mut self, tolerance: f64) {
533        self.tolerance = tolerance;
534    }
535
536    /// Get the bitmap scale.
537    pub fn bitmap_scale(&self) -> f64 {
538        self.bitmap_scale
539    }
540
541    /// Set the bitmap scale.
542    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        // Premultiply the color.
598        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        // Use optimized clear if possible.
614        if region.is_none() {
615            self.source.context.clear(self.device, self.queue, color);
616            return;
617        }
618
619        // Ignore clipping mask and transform.
620        let ignore_state = self.temporarily_ignore_state();
621
622        // Otherwise, fall back to filling in the screen rectangle.
623        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 we have a bitmap scale, scale the clip shape up.
681        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        // Iterate over the glyphs and use them to write.
713        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                // Combine the run's glyphs and the layout's y position.
724                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                    // Get the rectangle in texture space representing the glyph.
732                    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                            // Still waiting to load.
752                            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                    // Get the rectangle in screen space representing the glyph.
760                    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                    // Register the glyph in the atlas.
782                    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        // Create a rectangle for the destination and a rectangle for UV.
903        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        // Set the interpolation mode.
918        image
919            .texture()
920            .set_interpolation(&mut self.source.context, self.device, interp);
921
922        // Use this to draw the image.
923        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        // Create a new texture to copy the image to.
944        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        // Capture the area in the texture.
957        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        // Compute the blurred rectangle image.
988        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        // Create an image using this mask.
1002        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        // Draw this image.
1023        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
1076/// Convert a `piet::Shape` to a `tiny_skia` path.
1077fn 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}