Skip to main content

ferroui_vello/
drawing_context_impl.rs

1use crate::geometry_impl::{try_get_geometry_impl, GeometryImpl};
2use crate::glyph_run_impl::GlyphRunImpl;
3use crate::helpers::path_helper;
4use crate::i_drawable_bitmap_impl::try_get_drawable_bitmap;
5use crate::scene::{
6    IVelloSceneSink, VelloSceneBrush, VelloSceneGlyph, VelloSceneGlyphRun, VelloSceneImage, VelloScenePaint,
7    VelloScenePixelRect,
8};
9use crate::surface_render_target::{SurfaceRenderTarget, SurfaceRenderTargetCreateInfo};
10use crate::vello_extensions::{
11    ellipse_path, rect_path, rounded_rect_path, to_affine, to_blend_mode, to_color, to_color_with_opacity,
12    to_extend, to_fill, to_kurbo_point,
13};
14use crate::vello_options::{VelloOptions, VelloRenderingMode};
15use crate::vello_platform::VelloPlatform;
16use crate::vello_region_impl::VelloRegionImpl;
17use crate::vello_typeface::{bold_simulation_outline_width, OBLIQUE_SKEW};
18use ferroui_base::media::{
19    BaselinePixelAlignment, BoxShadows, Color, Colors, EdgeMode, FontSimulations, IBrush, IGradientBrush, IPen,
20    ITileBrush, RenderOptions, TextHintingMode, TextOptions, TextRenderingMode, TileMode,
21};
22use ferroui_base::platform::{
23    IBitmapImpl, IDrawingContextImpl, IDrawingContextLayerImpl, IGeometryImpl, IGlyphRunImpl,
24    IPlatformRenderInterfaceRegion,
25};
26use ferroui_base::rendering::utilities::TileBrushCalculator;
27use ferroui_base::utilities::MathUtilities;
28use ferroui_base::{
29    FerroLocator, LocatorExtensions, Matrix, PixelSize, Point, Rect, RelativeUnit, RoundedRect, Vector,
30};
31use kurbo::{Affine, BezPath, Stroke};
32use peniko::color::{AlphaColor, Srgb};
33use peniko::{BlendMode, Compose, Extend, Fill, Gradient, ImageData, ImageQuality, InterpolationAlphaSpace, Mix};
34use std::any::{Any, TypeId};
35use std::rc::Rc;
36
37mod acrylic;
38pub(crate) mod box_shadows;
39mod effects;
40mod scene_brushes;
41
42/// Context create info.
43pub struct CreateInfo {
44    /// The scene the context records into: of the size of the target, in
45    /// the rendering mode of the target.
46    pub sink: Box<dyn IVelloSceneSink>,
47
48    /// What the target holds from the frames before, when it holds
49    /// anything: it stays under the scene.
50    pub backdrop: Option<VelloBackdrop>,
51
52    /// Receives the scene when the context is disposed, to render it into
53    /// the target.
54    pub on_finished: Box<dyn FnOnce(&mut dyn IVelloSceneSink)>,
55
56    /// Makes DPI to be applied as a hidden matrix transform.
57    pub scale_drawing_to_dpi: bool,
58
59    /// Dpi for intermediate surfaces.
60    pub dpi: Vector,
61
62    /// The rendering modes of intermediate surfaces, in the order they are
63    /// tried.
64    pub rendering_modes: Vec<VelloRenderingMode>,
65}
66
67/// What a target holds when a drawing context is created for it, and how
68/// the scene of the context keeps it.
69///
70/// A scene replaces the pixels of its target. What the target held is kept
71/// in one of two ways:
72///
73/// * **The scene is composed over the target** by the renderer
74///   ([`IVelloSceneSink::retain_target`]), when the target can be drawn
75///   over where it is ([`composes_over_target`](Self::composes_over_target))
76///   and the first thing the context is asked for is a clear to
77///   transparent inside clips that are rectangles of pixels: what the
78///   compositor does to redraw the dirty part of a layer. Those rectangles
79///   are cleared in the target and nothing is copied: the cost of a frame
80///   is the cost of what changed.
81/// * **The scene starts with the content as a paint**
82///   ([`brush`](Self::brush)), in every other case: the first thing that is
83///   drawn is drawn over what the target held. This is exact for whatever
84///   follows (a clear inside a rounded clip takes out what the target
85///   held, which a scene composed over its target could not), and costs a
86///   copy of the target.
87///
88/// A clear outside every clip and layer starts the scene over: the content
89/// is not kept.
90pub struct VelloBackdrop {
91    /// Whether the scene of the context can be composed over what the
92    /// target holds: the one that renders the scene composes over the
93    /// target itself (the pixels of a surface in memory with the CPU sink,
94    /// a texture of the device with a sink of the device).
95    pub composes_over_target: bool,
96
97    /// The content as a brush whose pixels lie on the pixels of the target,
98    /// or `None` when the target holds nothing after all. Asked for only
99    /// when the scene is not composed over the target.
100    pub brush: Box<dyn FnOnce() -> Option<VelloSceneBrush>>,
101}
102
103impl VelloBackdrop {
104    /// The backdrop of a target that cannot be drawn over where it is: an
105    /// image of what it holds.
106    pub fn image(image: ImageData) -> Self {
107        Self {
108            composes_over_target: false,
109            brush: Box::new(move || {
110                Some(VelloSceneBrush::Image(VelloSceneImage {
111                    image,
112                    x_extend: Extend::Pad,
113                    y_extend: Extend::Pad,
114                    quality: ImageQuality::Low,
115                    alpha: 1.0,
116                }))
117            }),
118        }
119    }
120}
121
122/// An intermediate surface of a drawing context: what a tile brush or the
123/// content of a scene brush is drawn into before a shape is painted with
124/// it. For a scene that is drawn on a device it is a texture of the device
125/// ([`DeviceSurfaceRenderTarget`](crate::gpu::DeviceSurfaceRenderTarget)),
126/// drawn and painted with there; otherwise pixels in memory.
127pub(crate) enum IntermediateSurface {
128    /// Pixels in memory.
129    Memory(SurfaceRenderTarget),
130    /// A texture of the device of the scene.
131    #[cfg(any(feature = "hybrid", feature = "gpu"))]
132    Device(crate::gpu::DeviceSurfaceRenderTarget),
133}
134
135impl IntermediateSurface {
136    pub(crate) fn create_drawing_context(&self) -> Box<dyn IDrawingContextImpl> {
137        match self {
138            IntermediateSurface::Memory(surface) => IDrawingContextLayerImpl::create_drawing_context(surface),
139            #[cfg(any(feature = "hybrid", feature = "gpu"))]
140            IntermediateSurface::Device(surface) => IDrawingContextLayerImpl::create_drawing_context(surface),
141        }
142    }
143
144    /// The surface as a bitmap of the contract.
145    pub(crate) fn bitmap(&self) -> &dyn IBitmapImpl {
146        match self {
147            IntermediateSurface::Memory(surface) => surface,
148            #[cfg(any(feature = "hybrid", feature = "gpu"))]
149            IntermediateSurface::Device(surface) => surface,
150        }
151    }
152
153    /// The surface as a brush for the scene of `context`, with its size in
154    /// pixels: the texture where the scene is drawn on its device, an image
155    /// of the pixels otherwise.
156    pub(crate) fn brush(
157        &self,
158        context: &mut DrawingContextImpl,
159        extend: (Extend, Extend),
160        quality: ImageQuality,
161        alpha: f32,
162    ) -> Option<(VelloSceneBrush, PixelSize)> {
163        let _ = &context;
164        match self {
165            IntermediateSurface::Memory(surface) => {
166                let image = crate::i_drawable_bitmap_impl::IDrawableBitmapImpl::image(surface)?;
167                let size = PixelSize::new(image.width as i32, image.height as i32);
168                let (x_extend, y_extend) = extend;
169                Some((VelloSceneBrush::Image(VelloSceneImage { image, x_extend, y_extend, quality, alpha }), size))
170            }
171            #[cfg(any(feature = "hybrid", feature = "gpu"))]
172            IntermediateSurface::Device(surface) => {
173                Some((surface.brush(context, extend, quality, alpha)?, IBitmapImpl::pixel_size(surface)))
174            }
175        }
176    }
177
178    pub(crate) fn dispose(&self) {
179        IBitmapImpl::dispose(self.bitmap());
180    }
181}
182
183/// What the context remembers of a clip that is open.
184struct ClipRecord {
185    /// The clip as rectangles of pixels of the scene, when it is that: a
186    /// region, or a rectangle whose transform leaves its sides on whole
187    /// pixels.
188    rects: Option<Vec<VelloScenePixelRect>>,
189    /// The clip as it was pushed, while the scene may have to start over
190    /// with the content of the target under it.
191    pushed: Option<(BezPath, Fill, Affine, bool)>,
192}
193
194/// The stage of the design document a feature that is not built belongs to.
195///
196/// # Panics
197/// Always: a member of the contract that is not built yet fails rather than
198/// draw something else or nothing.
199#[track_caller]
200pub(crate) fn not_built(feature: &str, stage: &str) -> ! {
201    panic!("The Vello backend does not draw {feature} yet: {stage} of docs/porting/vello-backend.md")
202}
203
204/// What a pushed state has to end in the scene when it is popped.
205#[derive(Clone, Copy, Debug, PartialEq, Eq)]
206enum SavedKind {
207    /// A clip.
208    Clip,
209    /// A layer.
210    Layer,
211    /// Nothing: a state that only saved the transform.
212    Nothing,
213    /// An effect: its layer, and its scene when it has one of its own.
214    Effect,
215}
216
217/// Vello based drawing context.
218///
219/// The immediate calls of the contract are recorded into the scene of the
220/// frame ([`IVelloSceneSink`]); the scene is rendered when the context is
221/// disposed. The states the contract pushes and pops are a stack beside the
222/// scene, as the canvas of Skia keeps one: a pop restores the transform that
223/// was current at the push.
224pub struct DrawingContextImpl {
225    sink: Option<Box<dyn IVelloSceneSink>>,
226    on_finished: Option<Box<dyn FnOnce(&mut dyn IVelloSceneSink)>>,
227    disposables: Vec<Box<dyn FnOnce()>>,
228    // TODO: Get rid of this value, it's currently used to calculate
229    // intermediate sizes for tile brushes but does so ignoring the current
230    // transform.
231    intermediate_surface_dpi: Vector,
232    rendering_modes: Vec<VelloRenderingMode>,
233    state_stack: Vec<(Matrix, SavedKind)>,
234    /// For each state of `state_stack`, what is remembered of it when it is
235    /// a clip, while the content of the target is kept.
236    clip_records: Vec<Option<ClipRecord>>,
237    /// What the target holds, until it is decided how the scene keeps it
238    /// ([`VelloBackdrop`]).
239    backdrop: Option<VelloBackdrop>,
240    /// Whether the scene is composed over what the target holds.
241    retains_target: bool,
242    /// Whether anything was drawn into the scene of the target.
243    drawn: bool,
244    mask_stack: Vec<(Affine, PaintWrapper)>,
245    opacity_stack: Vec<(f64, bool)>,
246    render_options_stack: Vec<RenderOptions>,
247    text_options_stack: Vec<TextOptions>,
248    post_transform: Option<Matrix>,
249    current_opacity: f64,
250    current_transform: Matrix,
251    disposed: bool,
252    use_opacity_save_layer: bool,
253    render_options: RenderOptions,
254    text_options: TextOptions,
255    /// The effects that are pushed.
256    effect_stack: Vec<effects::EffectFrame>,
257    /// The clips that are open in the scene of the context.
258    open_clips: usize,
259    /// The pixel of the target that is the top left pixel of the scene of
260    /// the context: not the first one while an effect is recorded into a
261    /// scene of its own.
262    sink_origin: kurbo::Vec2,
263    /// When the context was created, while the phases of a frame are timed
264    /// ([`crate::perf`]).
265    created: Option<std::time::Instant>,
266}
267
268impl DrawingContextImpl {
269    /// Creates a new drawing context.
270    ///
271    /// `disposables` run in order after drawing has finished, when the
272    /// context is disposed.
273    pub fn new(create_info: CreateInfo, disposables: Vec<Box<dyn FnOnce()>>) -> Self {
274        let default_dpi = VelloPlatform::default_dpi();
275        let post_transform = if create_info.scale_drawing_to_dpi && !create_info.dpi.nearly_equals(default_dpi) {
276            Some(Matrix::create_scale(create_info.dpi.x / default_dpi.x, create_info.dpi.y / default_dpi.y))
277        } else {
278            None
279        };
280
281        // The options registered on this thread, else the ones the backend
282        // was initialized with (the render thread has no services).
283        let use_opacity_save_layer = FerroLocator::current()
284            .get_service::<VelloOptions>()
285            .map(|options| *options)
286            .or_else(VelloPlatform::options)
287            .is_some_and(|options| options.use_opacity_save_layer);
288
289        let context = Self {
290            sink: Some(create_info.sink),
291            on_finished: Some(create_info.on_finished),
292            disposables,
293            intermediate_surface_dpi: create_info.dpi,
294            rendering_modes: create_info.rendering_modes,
295            state_stack: Vec::new(),
296            clip_records: Vec::new(),
297            backdrop: create_info.backdrop,
298            retains_target: false,
299            drawn: false,
300            mask_stack: Vec::new(),
301            opacity_stack: Vec::new(),
302            render_options_stack: Vec::new(),
303            text_options_stack: Vec::new(),
304            post_transform,
305            current_opacity: 1.0,
306            current_transform: Matrix::IDENTITY,
307            disposed: false,
308            use_opacity_save_layer,
309            render_options: RenderOptions::default(),
310            text_options: TextOptions::default(),
311            effect_stack: Vec::new(),
312            open_clips: 0,
313            sink_origin: kurbo::Vec2::ZERO,
314            created: crate::perf::frame_start(),
315        };
316        crate::perf::count(crate::perf::Phase::Context, 0);
317
318        context
319    }
320
321    /// The scene, to draw into: what the target held is under whatever is
322    /// drawn now.
323    ///
324    /// # Panics
325    /// Panics when the drawing context has been disposed.
326    fn sink(&mut self) -> &mut dyn IVelloSceneSink {
327        if self.backdrop.is_some() {
328            self.start_with_backdrop();
329        }
330        self.drawn = true;
331        self.scene()
332    }
333
334    /// The scene, to ask it something or to open a clip in it: nothing is
335    /// decided about what the target held.
336    ///
337    /// # Panics
338    /// Panics when the drawing context has been disposed.
339    fn scene(&mut self) -> &mut dyn IVelloSceneSink {
340        match &mut self.sink {
341            Some(sink) => &mut **sink,
342            None => panic!("The drawing context has been disposed"),
343        }
344    }
345
346    /// Starts the scene with what the target held as a paint, under the
347    /// clips that are open (see [`VelloBackdrop`]).
348    fn start_with_backdrop(&mut self) {
349        let Some(backdrop) = self.backdrop.take() else {
350            return;
351        };
352
353        // The clips that are open were pushed into a scene that holds
354        // nothing else: the scene starts over with the content, and the
355        // clips are opened again above it.
356        let clips: Vec<(BezPath, Fill, Affine, bool)> =
357            self.clip_records.iter_mut().filter_map(|record| record.as_mut()?.pushed.take()).collect();
358
359        let Some(brush) = (backdrop.brush)() else {
360            return;
361        };
362
363        let (width, height) = (self.scene().width() as f64, self.scene().height() as f64);
364        let path = rect_path(Rect::new(0.0, 0.0, width, height));
365        let paint = VelloScenePaint { brush, transform: Affine::IDENTITY };
366
367        let scene = self.scene();
368        if !clips.is_empty() {
369            scene.reset();
370        }
371        scene.fill(&path, Fill::NonZero, Affine::IDENTITY, &paint, BlendMode::default(), false);
372        for (path, fill_rule, transform, anti_alias) in &clips {
373            scene.push_clip(path, *fill_rule, *transform, *anti_alias);
374        }
375    }
376
377    /// Whether the clips that are open are remembered: while the content of
378    /// the target is kept or may be.
379    fn records_clips(&self) -> bool {
380        self.backdrop.is_some() || self.retains_target
381    }
382
383    /// The pixels the open clips leave, when everything that is open is a
384    /// clip of rectangles of pixels.
385    fn clipped_pixel_rects(&self) -> Option<Vec<VelloScenePixelRect>> {
386        if self.clip_records.len() != self.state_stack.len() || self.clip_records.is_empty() {
387            return None;
388        }
389
390        let mut rects: Option<Vec<VelloScenePixelRect>> = None;
391        for record in &self.clip_records {
392            let clip = record.as_ref()?.rects.as_ref()?;
393            rects = Some(match rects {
394                None => clip.clone(),
395                Some(rects) => rects
396                    .iter()
397                    .flat_map(|rect| clip.iter().filter_map(move |other| rect.intersect(*other)))
398                    .collect(),
399            });
400        }
401        rects
402    }
403
404    /// A rectangle in the pixels of the scene as whole pixels, when its
405    /// sides are on whole pixels.
406    fn whole_pixels(rect: kurbo::Rect, width: u16, height: u16) -> Option<VelloScenePixelRect> {
407        let on_pixel = |value: f64| value.is_finite() && (value - value.round()).abs() < 1e-6;
408        if !(on_pixel(rect.x0) && on_pixel(rect.y0) && on_pixel(rect.x1) && on_pixel(rect.y1)) {
409            return None;
410        }
411        let clamp = |value: f64, limit: u16| value.round().clamp(0.0, f64::from(limit)) as u16;
412        Some(VelloScenePixelRect {
413            x0: clamp(rect.x0.min(rect.x1), width),
414            y0: clamp(rect.y0.min(rect.y1), height),
415            x1: clamp(rect.x0.max(rect.x1), width),
416            y1: clamp(rect.y0.max(rect.y1), height),
417        })
418    }
419
420    /// The rendering mode the context draws in.
421    pub fn rendering_mode(&mut self) -> VelloRenderingMode {
422        self.scene().rendering_mode()
423    }
424
425    /// Whether the scene of the context is drawn on `device` and paints
426    /// with its textures.
427    #[cfg(any(feature = "hybrid", feature = "gpu"))]
428    pub(crate) fn draws_on_device(&mut self, device: &std::sync::Arc<crate::gpu::VelloWgpuDevice>) -> bool {
429        let scene = self.scene();
430        scene.capabilities().device_textures && scene.device().is_some_and(|own| std::sync::Arc::ptr_eq(own, device))
431    }
432
433    /// The render options in effect.
434    pub fn render_options(&self) -> RenderOptions {
435        self.render_options
436    }
437
438    /// Replaces the render options in effect.
439    pub fn set_render_options(&mut self, value: RenderOptions) {
440        self.render_options = value;
441    }
442
443    /// The text options in effect.
444    pub fn text_options(&self) -> TextOptions {
445        self.text_options
446    }
447
448    /// Replaces the text options in effect.
449    pub fn set_text_options(&mut self, value: TextOptions) {
450        self.text_options = value;
451    }
452
453    /// The transform from the space the contract draws in to the pixels of
454    /// the target.
455    fn device_transform(&self) -> Affine {
456        let transform = match self.post_transform {
457            Some(post_transform) => to_affine(self.current_transform * post_transform),
458            None => to_affine(self.current_transform),
459        };
460
461        self.pixel_transform() * transform
462    }
463
464    /// The transform from the pixels of the target to the pixels of the
465    /// scene of the context.
466    fn pixel_transform(&self) -> Affine {
467        Affine::translate(-self.sink_origin)
468    }
469
470    fn anti_alias(&self) -> bool {
471        self.edge_anti_alias(self.render_options.edge_mode != EdgeMode::Aliased)
472    }
473
474    /// Whether an edge is anti-aliased when the contract asks for it to be
475    /// or not to be: as asked, and always in a rendering mode whose
476    /// renderer has no aliased edges at all. A renderer that has them for
477    /// rectangles on the axes only (classic `vello`, whose sink snaps such
478    /// rectangles to pixels) is asked as the contract asks: its sink draws
479    /// every other shape anti-aliased.
480    fn edge_anti_alias(&self, anti_alias: bool) -> bool {
481        anti_alias
482            || !self.sink.as_ref().is_some_and(|sink| {
483                let capabilities = sink.capabilities();
484                capabilities.aliased_edges || capabilities.aliased_rectangles
485            })
486    }
487
488    /// The whole target as a path, in pixels.
489    fn target_path(&mut self) -> BezPath {
490        let (width, height) = (self.scene().width() as f64, self.scene().height() as f64);
491        rect_path(Rect::new(0.0, 0.0, width, height))
492    }
493
494    /// Saves the transform with what the pop has to end in the scene.
495    fn save(&mut self, kind: SavedKind) {
496        self.save_with(kind, None);
497    }
498
499    /// [`save`](Self::save) with what is remembered of a clip.
500    fn save_with(&mut self, kind: SavedKind, record: Option<ClipRecord>) {
501        if kind == SavedKind::Clip {
502            self.open_clips += 1;
503        }
504        // The records are kept beside the states for as long as every state
505        // since the first has one or none: a context that keeps nothing of
506        // its target has none.
507        if self.clip_records.len() == self.state_stack.len() && (record.is_some() || !self.clip_records.is_empty()) {
508            self.clip_records.push(record);
509        }
510        self.state_stack.push((self.current_transform, kind));
511    }
512
513    /// Ends the innermost pushed state. The transform of the context is the
514    /// one that was current when the state was pushed.
515    fn restore(&mut self) {
516        if self.clip_records.len() == self.state_stack.len() {
517            self.clip_records.pop();
518        }
519        let (transform, kind) =
520            self.state_stack.pop().unwrap_or_else(|| panic!("The state stack of the drawing context is empty"));
521
522        match kind {
523            SavedKind::Clip => {
524                self.open_clips -= 1;
525                self.scene().pop_clip();
526            }
527            SavedKind::Layer => self.sink().pop_layer(),
528            SavedKind::Nothing => {}
529            SavedKind::Effect => self.end_effect(),
530        }
531
532        self.current_transform = transform;
533    }
534
535    fn push_clip_path(&mut self, path: &BezPath, fill_rule: Fill, anti_alias: bool) {
536        self.push_clip_path_with(path, fill_rule, anti_alias, None);
537    }
538
539    /// Opens a clip. `pixel_rect` is the clip in the space of the context
540    /// when it is a rectangle that may lie on whole pixels.
541    fn push_clip_path_with(&mut self, path: &BezPath, fill_rule: Fill, anti_alias: bool, rect: Option<Rect>) {
542        let (transform, anti_alias) = (self.device_transform(), self.edge_anti_alias(anti_alias));
543
544        let record = self.records_clips().then(|| {
545            let (width, height) = (self.scene().width(), self.scene().height());
546            let [_, b, c, _, _, _] = transform.as_coeffs();
547            let rects = rect
548                .filter(|_| b == 0.0 && c == 0.0)
549                .and_then(|rect| {
550                    let rect = kurbo::Rect::new(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
551                    Self::whole_pixels(transform.transform_rect_bbox(rect), width, height)
552                })
553                .map(|rect| vec![rect]);
554            ClipRecord {
555                rects,
556                pushed: self.backdrop.is_some().then(|| (path.clone(), fill_rule, transform, anti_alias)),
557            }
558        });
559
560        self.scene().push_clip(path, fill_rule, transform, anti_alias);
561        self.save_with(SavedKind::Clip, record);
562    }
563
564    /// Draws an image with its pixels on the pixels of the target, whatever
565    /// the transform of the context is.
566    pub(crate) fn draw_image_in_device_space(&mut self, image: ImageData, blend_mode: BlendMode) {
567        let (width, height) = (image.width as f64, image.height as f64);
568        let brush = VelloSceneBrush::Image(VelloSceneImage {
569            image,
570            x_extend: Extend::Pad,
571            y_extend: Extend::Pad,
572            quality: ImageQuality::Low,
573            alpha: 1.0,
574        });
575
576        self.draw_brush_in_device_space(brush, width, height, blend_mode);
577    }
578
579    /// Fills the rectangle of `width` by `height` pixels at the first pixel
580    /// of the target with a brush whose pixels lie on the pixels of the
581    /// target, whatever the transform of the context is.
582    pub(crate) fn draw_brush_in_device_space(
583        &mut self,
584        brush: VelloSceneBrush,
585        width: f64,
586        height: f64,
587        blend_mode: BlendMode,
588    ) {
589        let pixel_transform = self.pixel_transform();
590        let path = rect_path(Rect::new(0.0, 0.0, width, height));
591        let paint = VelloScenePaint { brush, transform: Affine::IDENTITY };
592
593        self.sink().fill(&path, Fill::NonZero, pixel_transform, &paint, blend_mode, false);
594    }
595
596    /// The relative transform of a brush conjugated into target space: the
597    /// unit square maps onto `target_rect`, the matrix acts inside that
598    /// space, before the absolute brush transform.
599    fn get_relative_transform(brush: &dyn IBrush, target_rect: Rect) -> Option<Matrix> {
600        let relative_transform = brush.relative_transform()?;
601        if target_rect.width <= 0.0 || target_rect.height <= 0.0 {
602            return None;
603        }
604
605        let matrix = relative_transform.value();
606        if matrix.is_identity() {
607            return None;
608        }
609
610        Some(
611            Matrix::create_translation(-target_rect.x, -target_rect.y)
612                * Matrix::create_scale(1.0 / target_rect.width, 1.0 / target_rect.height)
613                * matrix
614                * Matrix::create_scale(target_rect.width, target_rect.height)
615                * Matrix::create_translation(target_rect.x, target_rect.y),
616        )
617    }
618
619    /// The absolute transform of a brush around its transform origin.
620    fn get_absolute_transform(brush: &dyn IBrush, target_rect: Rect) -> Option<Matrix> {
621        let absolute_transform = brush.transform()?;
622        let transform_origin = brush.transform_origin().to_pixels_rect(target_rect);
623        let offset = Matrix::create_translation(transform_origin.x, transform_origin.y);
624        Some((-offset) * absolute_transform.value() * offset)
625    }
626
627    fn combine(first: Option<Matrix>, second: Option<Matrix>) -> Option<Matrix> {
628        match (first, second) {
629            (Some(first), Some(second)) => Some(first * second),
630            (first, second) => first.or(second),
631        }
632    }
633
634    /// Configures the paint wrapper for using a gradient brush.
635    ///
636    /// The gradients are those of the Skia backend, kind by kind: the same
637    /// points, radii and transforms, on the gradient kinds of peniko.
638    /// `opacity` is the opacity the brush is drawn with.
639    fn configure_gradient_brush(
640        paint_wrapper: &mut PaintWrapper,
641        target_rect: Rect,
642        gradient_brush: &dyn IGradientBrush,
643        opacity: f32,
644    ) {
645        let extend = to_extend(gradient_brush.spread_method());
646        let gradient_stops = gradient_brush.gradient_stops();
647        let mut stop_colors: Vec<AlphaColor<Srgb>> = gradient_stops.iter().map(|s| to_color(s.color())).collect();
648        let mut stop_offsets: Vec<f32> = gradient_stops.iter().map(|s| s.offset() as f32).collect();
649
650        if stop_colors.is_empty() {
651            // A gradient without stops: the Skia backend, which cannot
652            // build one, draws with the paint as it is, white at the
653            // opacity of the brush.
654            paint_wrapper.paint = Some(VelloScenePaint::solid(AlphaColor::WHITE.multiply_alpha(opacity)));
655            return;
656        }
657
658        let stops = |colors: &[AlphaColor<Srgb>], offsets: &[f32]| -> Vec<(f32, AlphaColor<Srgb>)> {
659            offsets.iter().copied().zip(colors.iter().copied()).collect()
660        };
661
662        // Skia interpolates the colors of the stops before it
663        // premultiplies them.
664        let finish = |mut gradient: Gradient, extend: Extend| {
665            gradient.extend = extend;
666            gradient.interpolation_alpha_space = InterpolationAlphaSpace::Unpremultiplied;
667            gradient
668        };
669
670        let mut underlay = None;
671
672        let (gradient, transform) = if let Some(linear_gradient) = gradient_brush.as_linear_gradient_brush() {
673            let start = to_kurbo_point(linear_gradient.start_point().to_pixels_rect(target_rect));
674            let end = to_kurbo_point(linear_gradient.end_point().to_pixels_rect(target_rect));
675
676            let transform = Self::combine(
677                Self::get_relative_transform(gradient_brush, target_rect),
678                Self::get_absolute_transform(gradient_brush, target_rect),
679            );
680
681            let gradient = Gradient::new_linear(start, end).with_stops(&stops(&stop_colors, &stop_offsets)[..]);
682            (finish(gradient, extend), transform)
683        } else if let Some(radial_gradient) = gradient_brush.as_radial_gradient_brush() {
684            let center_point = radial_gradient.center().to_pixels_rect(target_rect);
685            let center = to_kurbo_point(center_point);
686
687            let radius_x = radial_gradient.radius_x().to_value(target_rect.width);
688            let radius_y = radial_gradient.radius_y().to_value(target_rect.height);
689
690            let mut origin_point = radial_gradient.gradient_origin().to_pixels_rect(target_rect);
691
692            let mut transform: Option<Matrix> = None;
693
694            if radius_x != radius_y {
695                transform = Some(
696                    Matrix::create_translation(-center_point.x, -center_point.y)
697                        * Matrix::create_scale(1.0, radius_y / radius_x)
698                        * Matrix::create_translation(center_point.x, center_point.y),
699                );
700            }
701
702            transform = Self::combine(transform, Self::get_relative_transform(gradient_brush, target_rect));
703            transform = Self::combine(transform, Self::get_absolute_transform(gradient_brush, target_rect));
704
705            if origin_point == center_point {
706                // When the origin is the same as the center the radial
707                // gradient acts the same as D2D.
708                let gradient =
709                    Gradient::new_radial(center, radius_x as f32).with_stops(&stops(&stop_colors, &stop_offsets)[..]);
710                (finish(gradient, extend), transform)
711            } else {
712                // When the origin is different to the center use a two point
713                // conical gradient to match the behaviour of D2D.
714                if radius_x != radius_y {
715                    // Adjust the origin point for the radius x/y
716                    // transformation by reversing it.
717                    origin_point = origin_point
718                        .with_y((origin_point.y - center_point.y) * radius_x / radius_y + center_point.y);
719                }
720
721                let origin = to_kurbo_point(origin_point);
722
723                // The gradient leaves the area that no gradient circle reaches unpainted, which
724                // only happens when the origin is outside the circle. Everywhere else the
725                // gradient covers the whole area, and a backdrop would show through its
726                // transparent parts.
727                let origin_offset_x = origin_point.x - center_point.x;
728                let origin_offset_y = origin_point.y - center_point.y;
729                let origin_is_outside =
730                    origin_offset_x * origin_offset_x + origin_offset_y * origin_offset_y >= radius_x * radius_x;
731
732                let end_offset = stop_offsets[stop_offsets.len() - 1];
733
734                let mut start = origin;
735                let mut radius_start = 0f32;
736                let mut end = center;
737                let mut radius_end = radius_x as f32;
738                let reverse =
739                    (center_point.x != origin_point.x || center_point.y != origin_point.y) && end_offset == 1.0;
740
741                if reverse {
742                    // Reverse the order of the stops to match D2D.
743                    (start, radius_start, end, radius_end) = (end, radius_end, start, radius_start);
744
745                    let count = stop_offsets.len();
746
747                    let mut reversed_colors = vec![AlphaColor::TRANSPARENT; count];
748                    // And then reverse the reference point of the stops.
749                    let mut reversed_stops = vec![0f32; count];
750
751                    for i in 0..count {
752                        let mut offset = 1.0 - gradient_stops[i].offset();
753
754                        if MathUtilities::is_zero(offset) {
755                            offset = 0.0;
756                        }
757
758                        let reversed_index = count - 1 - i;
759
760                        reversed_stops[reversed_index] = offset as f32;
761                        reversed_colors[reversed_index] = stop_colors[i];
762                    }
763
764                    stop_colors = reversed_colors;
765                    stop_offsets = reversed_stops;
766                }
767
768                // Fill that unpainted area with the colour the gradient clamps to, like D2D
769                // does: the gradient is drawn over that color.
770                if origin_is_outside {
771                    underlay = Some(stop_colors[0]);
772                }
773
774                let gradient = Gradient::new_two_point_radial(start, radius_start, end, radius_end)
775                    .with_stops(&stops(&stop_colors, &stop_offsets)[..]);
776                (finish(gradient, extend), transform)
777            }
778        } else if let Some(conic_gradient) = gradient_brush.as_conic_gradient_brush() {
779            let center_point = conic_gradient.center().to_pixels_rect(target_rect);
780
781            // The angle 0 of a sweep gradient is to the right of the center
782            // point but we are matching CSS where the vertical point above
783            // the center is 0.
784            let angle = (conic_gradient.angle() - 90.0).to_radians();
785            let rotation = Matrix::create_rotation_at(angle, center_point);
786
787            let transform = Self::combine(
788                Some(rotation),
789                Self::combine(
790                    Self::get_relative_transform(gradient_brush, target_rect),
791                    Self::get_absolute_transform(gradient_brush, target_rect),
792                ),
793            );
794
795            let gradient = Gradient::new_sweep(to_kurbo_point(center_point), 0.0, std::f32::consts::TAU)
796                .with_stops(&stops(&stop_colors, &stop_offsets)[..]);
797            (finish(gradient, Extend::Pad), transform)
798        } else {
799            // A gradient of a kind the contract does not have draws nothing.
800            return;
801        };
802
803        let transform = transform.map(to_affine).unwrap_or(Affine::IDENTITY);
804
805        match underlay {
806            // The color and the gradient over it are composed as one, then
807            // the opacity of the brush applies to both.
808            Some(underlay) => {
809                paint_wrapper.underlay = Some(underlay);
810                paint_wrapper.layer_opacity = (opacity < 1.0).then_some(opacity);
811                paint_wrapper.paint = Some(VelloScenePaint { brush: VelloSceneBrush::Gradient(gradient), transform });
812            }
813            None => {
814                let gradient = if opacity < 1.0 { gradient.multiply_alpha(opacity) } else { gradient };
815                paint_wrapper.paint = Some(VelloScenePaint { brush: VelloSceneBrush::Gradient(gradient), transform });
816            }
817        }
818    }
819
820    /// How a tile brush continues in x and y.
821    fn get_tile_modes(mode: TileMode) -> (Extend, Extend) {
822        (
823            if mode == TileMode::FlipX || mode == TileMode::FlipXY { Extend::Reflect } else { Extend::Repeat },
824            if mode == TileMode::FlipY || mode == TileMode::FlipXY { Extend::Reflect } else { Extend::Repeat },
825        )
826    }
827
828    /// Configures the paint wrapper for using a tile brush.
829    ///
830    /// The tile is rendered into an intermediate surface whose pixels become
831    /// the image of the paint.
832    fn configure_tile_brush(
833        &mut self,
834        paint_wrapper: &mut PaintWrapper,
835        target_box: Rect,
836        tile_brush: &dyn ITileBrush,
837        tile_brush_image: &dyn IBitmapImpl,
838        opacity: f32,
839    ) {
840        let dpi = self.intermediate_surface_dpi;
841        let image_pixel_size = tile_brush_image.pixel_size();
842
843        let calc =
844            TileBrushCalculator::from_brush(tile_brush, image_pixel_size.to_size_with_dpi_vector(dpi), target_box.size());
845        let intermediate_size = PixelSize::from_size_with_dpi_vector(calc.intermediate_size(), dpi);
846        if intermediate_size.width < 1 || intermediate_size.height < 1 {
847            return;
848        }
849        crate::perf::count(
850            crate::perf::Phase::BrushSurface,
851            intermediate_size.width as u64 * intermediate_size.height as u64 * 4,
852        );
853        let intermediate = self.create_render_target(intermediate_size, true);
854
855        {
856            let mut context = intermediate.create_drawing_context();
857
858            let source_rect = Rect::from_size(image_pixel_size.to_size_with_dpi(96.0));
859            let target_rect = Rect::from_size(image_pixel_size.to_size_with_dpi_vector(dpi));
860
861            context.clear(Colors::TRANSPARENT);
862            context.push_clip(calc.intermediate_clip());
863            context.push_render_options(self.render_options);
864
865            context.set_transform(calc.intermediate_transform());
866
867            context.draw_bitmap(tile_brush_image, 1.0, source_rect, target_rect);
868
869            context.pop_render_options();
870            context.pop_clip();
871            context.dispose();
872        }
873
874        let tile_mode = tile_brush.tile_mode();
875
876        let tile_transform = if tile_mode != TileMode::None {
877            Matrix::create_translation(-calc.destination_rect().x, -calc.destination_rect().y)
878        } else {
879            Matrix::IDENTITY
880        };
881
882        // The Skia backend samples the tile with its default sampling: the
883        // nearest pixel.
884        let (x_extend, y_extend) = Self::get_tile_modes(tile_mode);
885        let brush = intermediate.brush(self, (x_extend, y_extend), ImageQuality::Low, opacity);
886        intermediate.dispose();
887        let Some((brush, tile_size)) = brush else {
888            return;
889        };
890
891        // From the pixels of the tile to the space of the shape: the
892        // position of a relative destination, the pixels as logical units,
893        // the origin of the tiling.
894        let mut paint_transform = Matrix::create_scale(96.0 / dpi.x, 96.0 / dpi.y) * tile_transform;
895
896        if tile_brush.destination_rect().unit == RelativeUnit::Relative {
897            paint_transform = Matrix::create_translation(target_box.x, target_box.y) * paint_transform;
898        }
899
900        // Both brush transforms act on the tile once it sits in target
901        // space, the relative one first.
902        if let Some(relative_transform) = Self::get_relative_transform(tile_brush, target_box) {
903            paint_transform = paint_transform * relative_transform;
904        }
905
906        if let Some(transform) = Self::get_absolute_transform(tile_brush, target_box) {
907            paint_transform = paint_transform * transform;
908        }
909
910        let paint_transform = to_affine(paint_transform);
911
912        if tile_mode == TileMode::None {
913            // A tile that is not repeated paints nothing beside itself. An
914            // image of peniko has no such extend mode (Skia's is "decal"):
915            // the shape is clipped to the tile instead.
916            let mut tile = rect_path(Rect::new(0.0, 0.0, tile_size.width as f64, tile_size.height as f64));
917            tile.apply_affine(paint_transform);
918            paint_wrapper.clip = Some(tile);
919        }
920
921        paint_wrapper.paint = Some(VelloScenePaint { brush, transform: paint_transform });
922    }
923
924    /// Creates a paint wrapper for the given brush.
925    fn create_paint(&mut self, brush: &dyn IBrush, target_rect: Rect) -> PaintWrapper {
926        let opacity = brush.opacity() * if self.use_opacity_save_layer { 1.0 } else { self.current_opacity };
927        let mut paint_wrapper = PaintWrapper::new();
928
929        if let Some(solid) = brush.as_solid_color_brush() {
930            paint_wrapper.paint = Some(VelloScenePaint::solid(to_color_with_opacity(solid.color(), opacity)));
931
932            return paint_wrapper;
933        }
934
935        // The opacity of a paint of Skia is the alpha of its color: a byte.
936        let opacity = ((255.0 * opacity) as u8) as f32 / 255.0;
937
938        if let Some(gradient) = brush.as_gradient_brush() {
939            Self::configure_gradient_brush(&mut paint_wrapper, target_rect, gradient, opacity);
940
941            return paint_wrapper;
942        }
943
944        let tile_brush = brush.as_tile_brush();
945        let mut tile_brush_image: Option<std::sync::Arc<ferroui_base::platform::SharedBitmapImpl>> = None;
946
947        if let Some(scene_brush) = brush.as_scene_brush() {
948            // A scene brush without content paints nothing.
949            if let Some(content) = scene_brush.create_content() {
950                self.configure_scene_brush_content(&mut paint_wrapper, &*content, target_rect, opacity);
951                content.dispose();
952                return paint_wrapper;
953            }
954        } else if let Some(image_brush) = brush.as_image_brush() {
955            tile_brush_image = image_brush
956                .source()
957                .and_then(|source| source.get_bitmap())
958                .filter(|bitmap| try_get_drawable_bitmap(&**bitmap).is_some());
959        }
960
961        // A tile brush without an image paints nothing.
962        if let (Some(tile_brush), Some(tile_brush_image)) = (tile_brush, tile_brush_image) {
963            self.configure_tile_brush(&mut paint_wrapper, target_rect, tile_brush, &*tile_brush_image, opacity);
964        }
965
966        paint_wrapper
967    }
968
969    /// Creates a paint wrapper and the stroke style for the given pen, or
970    /// `None` when the pen draws nothing.
971    fn create_stroke(&mut self, pen: &dyn IPen, target_rect: Rect) -> Option<(PaintWrapper, Stroke)> {
972        // A stroke without a thickness is a hairline to a renderer - and for
973        // us it means - there is nothing rendered.
974        let brush = pen.brush()?;
975        if pen.thickness() == 0.0 {
976            return None;
977        }
978
979        let paint_wrapper = self.create_paint(&*brush, target_rect);
980
981        Some((paint_wrapper, path_helper::create_stroke(pen)))
982    }
983
984    /// Draws with a paint wrapper: `draw` is called once for everything the
985    /// wrapper paints with, between the clip and the layer the wrapper
986    /// needs, and the wrapper is released.
987    fn draw_with(
988        &mut self,
989        paint_wrapper: PaintWrapper,
990        draw: &dyn Fn(&mut dyn IVelloSceneSink, &VelloScenePaint, Affine, bool),
991    ) {
992        let (transform, anti_alias) = (self.device_transform(), self.anti_alias());
993        let sink = self.sink();
994
995        if paint_wrapper.paint.is_some() || paint_wrapper.underlay.is_some() {
996            if let Some(clip) = &paint_wrapper.clip {
997                sink.push_clip(clip, Fill::NonZero, transform, anti_alias);
998            }
999            if let Some(opacity) = paint_wrapper.layer_opacity {
1000                sink.push_layer(BlendMode::default(), opacity);
1001            }
1002
1003            if let Some(underlay) = paint_wrapper.underlay {
1004                draw(sink, &VelloScenePaint::solid(underlay), transform, anti_alias);
1005            }
1006            if let Some(paint) = &paint_wrapper.paint {
1007                draw(sink, paint, transform, anti_alias);
1008            }
1009
1010            if paint_wrapper.layer_opacity.is_some() {
1011                sink.pop_layer();
1012            }
1013            if paint_wrapper.clip.is_some() {
1014                sink.pop_clip();
1015            }
1016        }
1017
1018        paint_wrapper.dispose();
1019    }
1020
1021    fn fill_path(&mut self, brush: &dyn IBrush, target_rect: Rect, path: &BezPath, fill_rule: Fill) {
1022        let fill = self.create_paint(brush, target_rect);
1023        self.draw_with(fill, &|sink, paint, transform, anti_alias| {
1024            sink.fill(path, fill_rule, transform, paint, BlendMode::default(), anti_alias);
1025        });
1026    }
1027
1028    fn stroke_path(&mut self, pen: &dyn IPen, target_rect: Rect, path: &BezPath) {
1029        if let Some((paint_wrapper, stroke)) = self.create_stroke(pen, target_rect) {
1030            self.draw_with(paint_wrapper, &|sink, paint, transform, anti_alias| {
1031                sink.stroke(path, &stroke, transform, paint, anti_alias);
1032            });
1033        }
1034    }
1035
1036    /// Creates a new render target compatible with this drawing context: a
1037    /// texture of the device for a scene that is drawn on one, pixels in
1038    /// memory otherwise.
1039    ///
1040    /// `use_scaled_drawing` auto-scales the drawing to the DPI.
1041    fn create_render_target(&mut self, pixel_size: PixelSize, use_scaled_drawing: bool) -> IntermediateSurface {
1042        #[cfg(any(feature = "hybrid", feature = "gpu"))]
1043        {
1044            let scene = self.scene();
1045            let mode = scene.rendering_mode();
1046            if let Some(device) = scene.device().cloned() {
1047                if crate::gpu::DeviceSurfaceRenderTarget::is_available(mode, &device) {
1048                    return IntermediateSurface::Device(crate::gpu::DeviceSurfaceRenderTarget::new(
1049                        device,
1050                        mode,
1051                        pixel_size,
1052                        self.intermediate_surface_dpi,
1053                        self.rendering_modes.clone(),
1054                        use_scaled_drawing,
1055                    ));
1056                }
1057            }
1058        }
1059
1060        IntermediateSurface::Memory(self.create_memory_render_target(pixel_size, use_scaled_drawing))
1061    }
1062
1063    fn create_memory_render_target(&self, pixel_size: PixelSize, use_scaled_drawing: bool) -> SurfaceRenderTarget {
1064        SurfaceRenderTarget::new(SurfaceRenderTargetCreateInfo {
1065            width: pixel_size.width,
1066            height: pixel_size.height,
1067            dpi: self.intermediate_surface_dpi,
1068            rendering_modes: self.rendering_modes.clone(),
1069            use_scaled_drawing,
1070        })
1071    }
1072
1073    /// Draws a surface of the device of the scene as a paint with its
1074    /// texture. A texture has no levels of a mipmap: a mode with mipmaps is
1075    /// sampled bilinearly.
1076    #[cfg(any(feature = "hybrid", feature = "gpu"))]
1077    fn draw_device_surface(
1078        &mut self,
1079        surface: &crate::gpu::DeviceSurfaceRenderTarget,
1080        opacity: f64,
1081        source_rect: Rect,
1082        dest_rect: Rect,
1083    ) {
1084        if source_rect.width <= 0.0 || source_rect.height <= 0.0 || dest_rect.width <= 0.0 || dest_rect.height <= 0.0 {
1085            return;
1086        }
1087
1088        let image_transform = Affine::translate((dest_rect.x, dest_rect.y))
1089            * Affine::scale_non_uniform(dest_rect.width / source_rect.width, dest_rect.height / source_rect.height)
1090            * Affine::translate((-source_rect.x, -source_rect.y));
1091        let is_upscaling = dest_rect.width > source_rect.width || dest_rect.height > source_rect.height;
1092        let (quality, _) =
1093            crate::vello_extensions::to_sampling(self.render_options.bitmap_interpolation_mode, is_upscaling);
1094        let alpha = ((255.0 * opacity * self.current_opacity) as u8) as f32 / 255.0;
1095        let blend_mode = to_blend_mode(self.render_options.bitmap_blending_mode);
1096        let (transform, anti_alias) = (self.device_transform(), self.anti_alias());
1097
1098        let Some(brush) = surface.brush(self, (Extend::Pad, Extend::Pad), quality, alpha) else {
1099            return;
1100        };
1101        let paint = VelloScenePaint { brush, transform: image_transform };
1102        self.sink().fill(&rect_path(dest_rect), Fill::NonZero, transform, &paint, blend_mode, anti_alias);
1103    }
1104
1105    fn geometry_impl(geometry: &dyn IGeometryImpl) -> &dyn GeometryImpl {
1106        try_get_geometry_impl(geometry)
1107            .unwrap_or_else(|| panic!("The geometry was not created by the Vello backend"))
1108    }
1109
1110    fn region_impl(region: &dyn IPlatformRenderInterfaceRegion) -> &VelloRegionImpl {
1111        region
1112            .as_any()
1113            .downcast_ref::<VelloRegionImpl>()
1114            .unwrap_or_else(|| panic!("The region was not created by the Vello backend"))
1115    }
1116
1117    fn uses_opacity_layer(&self) -> bool {
1118        self.use_opacity_save_layer || self.render_options.requires_full_opacity_handling == Some(true)
1119    }
1120}
1121
1122impl IDrawingContextImpl for DrawingContextImpl {
1123    fn transform(&self) -> Matrix {
1124        self.current_transform
1125    }
1126
1127    fn set_transform(&mut self, value: Matrix) {
1128        self.current_transform = value;
1129    }
1130
1131    fn clear(&mut self, color: Color) {
1132        // Outside every clip and layer a clear replaces all there is: the
1133        // scene starts over, and nothing of the target is kept.
1134        if self.state_stack.is_empty() && self.mask_stack.is_empty() {
1135            self.backdrop = None;
1136            self.retains_target = false;
1137            self.clip_records.clear();
1138            self.scene().reset();
1139            self.drawn = false;
1140            if color.a == 0 {
1141                return;
1142            }
1143        } else if color.a == 0 && self.mask_stack.is_empty() {
1144            // Inside clips that are rectangles of pixels, a clear to
1145            // transparent of a target that is kept is a clear of those
1146            // pixels of the target: the scene is composed over the rest
1147            // (see `VelloBackdrop`).
1148            let composes = self.retains_target
1149                || (self.backdrop.as_ref().is_some_and(|backdrop| backdrop.composes_over_target)
1150                    && self.scene().capabilities().retained_targets);
1151            if composes {
1152                if let Some(rects) = self.clipped_pixel_rects() {
1153                    self.backdrop = None;
1154                    self.retains_target = true;
1155                    for record in self.clip_records.iter_mut().flatten() {
1156                        record.pushed = None;
1157                    }
1158                    self.scene().retain_target(&rects);
1159                    // What the scene itself drew there goes as well.
1160                    if !self.drawn {
1161                        return;
1162                    }
1163                }
1164            } else if self.backdrop.is_none() && !self.drawn {
1165                // Nothing is under the clear: the target holds nothing that
1166                // is kept, and nothing was drawn.
1167                return;
1168            }
1169        }
1170
1171        // The pixels inside the clip are replaced, not blended.
1172        let path = self.target_path();
1173        let paint = VelloScenePaint::solid(to_color(color));
1174        self.sink().fill(&path, Fill::NonZero, Affine::IDENTITY, &paint, BlendMode::new(Mix::Normal, Compose::Copy), false);
1175    }
1176
1177    fn draw_bitmap(&mut self, source: &dyn IBitmapImpl, opacity: f64, source_rect: Rect, dest_rect: Rect) {
1178        // A surface of the device of the scene is drawn from where it is.
1179        #[cfg(any(feature = "hybrid", feature = "gpu"))]
1180        if let Some(surface) = source.as_any().downcast_ref::<crate::gpu::DeviceSurfaceRenderTarget>() {
1181            if self.draws_on_device(surface.device()) {
1182                self.draw_device_surface(surface, opacity, source_rect, dest_rect);
1183                return;
1184            }
1185        }
1186
1187        let drawable_image = try_get_drawable_bitmap(source)
1188            .unwrap_or_else(|| panic!("The bitmap was not created by the Vello backend"));
1189        let Some(image) = drawable_image.image() else {
1190            return;
1191        };
1192        if source_rect.width <= 0.0 || source_rect.height <= 0.0 || dest_rect.width <= 0.0 || dest_rect.height <= 0.0 {
1193            return;
1194        }
1195
1196        // The source rectangle of the image onto the destination rectangle.
1197        let mut image_transform = Affine::translate((dest_rect.x, dest_rect.y))
1198            * Affine::scale_non_uniform(dest_rect.width / source_rect.width, dest_rect.height / source_rect.height)
1199            * Affine::translate((-source_rect.x, -source_rect.y));
1200
1201        let is_upscaling = dest_rect.width > source_rect.width || dest_rect.height > source_rect.height;
1202        let (quality, mipmaps) =
1203            crate::vello_extensions::to_sampling(self.render_options.bitmap_interpolation_mode, is_upscaling);
1204
1205        let alpha = ((255.0 * opacity * self.current_opacity) as u8) as f32 / 255.0;
1206        let blend_mode = to_blend_mode(self.render_options.bitmap_blending_mode);
1207        let (transform, anti_alias) = (self.device_transform(), self.anti_alias());
1208
1209        // An image that is drawn reduced in a mode with mipmaps is sampled
1210        // from the two levels of its mipmap that are nearest to the size
1211        // it is drawn at.
1212        let mut image = image;
1213        if mipmaps {
1214            use crate::helpers::mipmap_helper;
1215
1216            if let Some(levels) = mipmap_helper::levels(&image, transform * image_transform) {
1217                if blend_mode == BlendMode::default() {
1218                    let path = rect_path(dest_rect);
1219                    mipmap_helper::fill_with_levels(
1220                        self.sink(),
1221                        &path,
1222                        transform,
1223                        image_transform,
1224                        &levels,
1225                        alpha,
1226                        anti_alias,
1227                    );
1228                    return;
1229                }
1230
1231                // Another blending mode composes the image with what is
1232                // under it inside the rectangle only, which a layer of the
1233                // two levels would not: one image stands for both.
1234                if let Some(prefiltered) = levels.blended() {
1235                    image_transform *=
1236                        Affine::scale_non_uniform(1.0 / prefiltered.scale_x, 1.0 / prefiltered.scale_y);
1237                    image = prefiltered.image;
1238                }
1239            }
1240        }
1241
1242        let paint = VelloScenePaint {
1243            brush: VelloSceneBrush::Image(VelloSceneImage {
1244                image,
1245                x_extend: Extend::Pad,
1246                y_extend: Extend::Pad,
1247                quality,
1248                alpha,
1249            }),
1250            transform: image_transform,
1251        };
1252
1253        self.sink().fill(&rect_path(dest_rect), Fill::NonZero, transform, &paint, blend_mode, anti_alias);
1254    }
1255
1256    fn draw_bitmap_with_mask(
1257        &mut self,
1258        source: &dyn IBitmapImpl,
1259        opacity_mask: &dyn IBrush,
1260        opacity_mask_rect: Rect,
1261        dest_rect: Rect,
1262    ) {
1263        self.push_opacity_mask(opacity_mask, opacity_mask_rect);
1264        let pixel_size = source.pixel_size();
1265        self.draw_bitmap(
1266            source,
1267            1.0,
1268            Rect::new(0.0, 0.0, pixel_size.width as f64, pixel_size.height as f64),
1269            dest_rect,
1270        );
1271        self.pop_opacity_mask();
1272    }
1273
1274    fn draw_line(&mut self, pen: Option<&dyn IPen>, p1: Point, p2: Point) {
1275        let Some(pen) = pen else {
1276            return;
1277        };
1278
1279        let mut path = BezPath::new();
1280        path.move_to(to_kurbo_point(p1));
1281        path.line_to(to_kurbo_point(p2));
1282
1283        self.stroke_path(pen, Rect::from_points(p1, p2).normalize(), &path);
1284    }
1285
1286    fn draw_geometry(&mut self, brush: Option<&dyn IBrush>, pen: Option<&dyn IPen>, geometry: &dyn IGeometryImpl) {
1287        let geometry_impl = Self::geometry_impl(geometry);
1288        let rect = geometry.bounds();
1289
1290        if let (Some(brush), Some(fill_path)) = (brush, geometry_impl.fill_path()) {
1291            self.fill_path(brush, rect, fill_path.path(), to_fill(fill_path.fill_rule()));
1292        }
1293
1294        if let (Some(pen), Some(stroke_path)) = (pen, geometry_impl.stroke_path()) {
1295            self.stroke_path(pen, rect.inflate(pen.thickness() / 2.0), stroke_path.path());
1296        }
1297    }
1298
1299    fn draw_rectangle(
1300        &mut self,
1301        brush: Option<&dyn IBrush>,
1302        pen: Option<&dyn IPen>,
1303        rect: RoundedRect,
1304        box_shadows: &BoxShadows,
1305    ) {
1306        if rect.rect.height <= 0.0 || rect.rect.width <= 0.0 {
1307            return;
1308        }
1309
1310        // Arbitrary chosen values, as in the Skia backend: a box that is
1311        // this large has no shadows.
1312        let no_box_shadows = BoxShadows::default();
1313        let box_shadows =
1314            if rect.rect.height > 8192.0 || rect.rect.width > 8192.0 { &no_box_shadows } else { box_shadows };
1315
1316        self.draw_box_shadows(&rect, box_shadows, false);
1317
1318        let path = rounded_rect_path(rect);
1319
1320        if let Some(brush) = brush {
1321            self.fill_path(brush, rect.rect, &path, Fill::NonZero);
1322        }
1323
1324        self.draw_box_shadows(&rect, box_shadows, true);
1325
1326        if let Some(pen) = pen {
1327            self.stroke_path(pen, rect.rect.inflate(pen.thickness() / 2.0), &path);
1328        }
1329    }
1330
1331    fn draw_region(
1332        &mut self,
1333        brush: Option<&dyn IBrush>,
1334        pen: Option<&dyn IPen>,
1335        region: &dyn IPlatformRenderInterfaceRegion,
1336    ) {
1337        let r = Self::region_impl(region);
1338        if r.is_empty() {
1339            return;
1340        }
1341
1342        let bounds = r.bounds().to_pixel_rect().to_rect(1.0);
1343        let path = r.path();
1344
1345        if let Some(brush) = brush {
1346            self.fill_path(brush, bounds, &path, Fill::NonZero);
1347        }
1348
1349        if let Some(pen) = pen {
1350            self.stroke_path(pen, bounds.inflate(pen.thickness() / 2.0), &path);
1351        }
1352    }
1353
1354    fn draw_ellipse(&mut self, brush: Option<&dyn IBrush>, pen: Option<&dyn IPen>, rect: Rect) {
1355        if rect.height <= 0.0 || rect.width <= 0.0 {
1356            return;
1357        }
1358
1359        let path = ellipse_path(rect);
1360
1361        if let Some(brush) = brush {
1362            self.fill_path(brush, rect, &path, Fill::NonZero);
1363        }
1364
1365        if let Some(pen) = pen {
1366            self.stroke_path(pen, rect.inflate(pen.thickness() / 2.0), &path);
1367        }
1368    }
1369
1370    fn draw_glyph_run(&mut self, foreground: Option<&dyn IBrush>, glyph_run: &dyn IGlyphRunImpl) {
1371        let Some(foreground) = foreground else {
1372            return;
1373        };
1374
1375        let glyph_run_impl = glyph_run
1376            .as_any()
1377            .downcast_ref::<GlyphRunImpl>()
1378            .unwrap_or_else(|| panic!("The glyph run was not created by the Vello backend"));
1379
1380        // A font no renderer draws glyphs of (see `VelloFontFace`).
1381        if !glyph_run_impl.face().is_drawable() {
1382            return;
1383        }
1384
1385        let paint_wrapper = self.create_paint(foreground, glyph_run.bounds());
1386
1387        // Determine the effective text options for text rendering. Start
1388        // with the current pushed text options.
1389        let mut effective_text_options = self.text_options;
1390
1391        // If the text rendering mode is unspecified in the text options, use
1392        // the one from the render options, and without one there the edge
1393        // mode, as the glyph run of the Skia backend does.
1394        if effective_text_options.text_rendering_mode == TextRenderingMode::Unspecified {
1395            effective_text_options.text_rendering_mode =
1396                if self.render_options.text_rendering_mode != TextRenderingMode::Unspecified {
1397                    self.render_options.text_rendering_mode
1398                } else if self.render_options.edge_mode == EdgeMode::Aliased {
1399                    TextRenderingMode::Alias
1400                } else {
1401                    TextRenderingMode::SubpixelAntialias
1402                };
1403        }
1404
1405        // No renderer of the Vello project draws text for the sub-pixels of
1406        // a display: the sub-pixel mode is the grey-scale one here, as it
1407        // is in the Skia backend where sub-pixel text is disabled.
1408        let anti_alias = effective_text_options.text_rendering_mode != TextRenderingMode::Alias;
1409
1410        // Hinting as far as the renderer has it: the light and the strong
1411        // mode are its one, vertical, mode.
1412        let hint = effective_text_options.text_hinting_mode != TextHintingMode::None;
1413
1414        // Sub-pixel positioning is enabled when the edging is not alias,
1415        // and the baseline is on a row of pixels unless that is disabled.
1416        // Both are meant in pixels: they apply while the text is upright.
1417        let transform = self.device_transform();
1418        let [a, b, c, d, e, f] = transform.as_coeffs();
1419        let upright = b == 0.0 && c == 0.0 && a > 0.0 && d > 0.0;
1420
1421        let snap_baseline =
1422            upright && effective_text_options.baseline_pixel_alignment != BaselinePixelAlignment::Unaligned;
1423        let whole_pixels = upright && !anti_alias;
1424
1425        let origin = glyph_run.baseline_origin();
1426        let snap_x = |x: f64| if whole_pixels { ((a * x + e).round() - e) / a } else { x };
1427        let snap_y = |y: f64| if snap_baseline { ((d * y + f).round() - f) / d } else { y };
1428
1429        let glyphs: Vec<VelloSceneGlyph> = glyph_run_impl
1430            .glyph_indices()
1431            .iter()
1432            .zip(glyph_run_impl.glyph_positions())
1433            .map(|(glyph_index, (x, y))| VelloSceneGlyph {
1434                id: *glyph_index as u32,
1435                x: snap_x(origin.x + *x as f64) as f32,
1436                y: (snap_y(origin.y) + *y as f64) as f32,
1437            })
1438            .collect();
1439
1440        let face = glyph_run_impl.face();
1441        let em_size = glyph_run.font_rendering_em_size();
1442        let font_simulations = face.font_simulations();
1443
1444        let scene_glyph_run = VelloSceneGlyphRun {
1445            font: face.data(),
1446            font_size: em_size as f32,
1447            units_per_em: face.units_per_em(),
1448            normalized_coords: face.normalized_coord_bits(),
1449            glyphs: &glyphs,
1450            embolden: if font_simulations.contains(FontSimulations::Bold) {
1451                bold_simulation_outline_width(em_size) / 2.0
1452            } else {
1453                0.0
1454            },
1455            skew: if font_simulations.contains(FontSimulations::Oblique) { OBLIQUE_SKEW } else { 0.0 },
1456            hint,
1457        };
1458
1459        self.draw_with(paint_wrapper, &|sink, paint, transform, _| {
1460            sink.draw_glyph_run(&scene_glyph_run, transform, paint, anti_alias);
1461        });
1462    }
1463
1464    fn create_layer(&mut self, size: PixelSize) -> Rc<dyn IDrawingContextLayerImpl> {
1465        // A layer of a scene that is drawn on a device stays on the device.
1466        #[cfg(any(feature = "hybrid", feature = "gpu"))]
1467        {
1468            let scene = self.scene();
1469            let mode = scene.rendering_mode();
1470            if let Some(device) = scene.device().cloned() {
1471                if crate::gpu::DeviceSurfaceRenderTarget::is_available(mode, &device) {
1472                    return Rc::new(crate::gpu::DeviceSurfaceRenderTarget::new(
1473                        device,
1474                        mode,
1475                        size,
1476                        self.intermediate_surface_dpi,
1477                        self.rendering_modes.clone(),
1478                        false,
1479                    ));
1480                }
1481            }
1482        }
1483
1484        Rc::new(self.create_memory_render_target(size, false))
1485    }
1486
1487    fn push_clip(&mut self, clip: Rect) {
1488        // The edges of a rectangle clip are not anti-aliased, those of a
1489        // rounded rectangle and of a geometry are, whatever the edge mode
1490        // is: what the Skia backend asks of its canvas.
1491        self.push_clip_path_with(&rect_path(clip), Fill::NonZero, false, Some(clip));
1492    }
1493
1494    fn push_clip_rounded(&mut self, clip: RoundedRect) {
1495        self.push_clip_path(&rounded_rect_path(clip), Fill::NonZero, true);
1496    }
1497
1498    fn push_clip_region(&mut self, region: &dyn IPlatformRenderInterfaceRegion) {
1499        let path = Self::region_impl(region).path();
1500
1501        // A region is a set of pixels of the target: it is not transformed
1502        // and has no edge inside a pixel.
1503        let (pixel_transform, anti_alias) = (self.pixel_transform(), self.edge_anti_alias(false));
1504
1505        let record = self.records_clips().then(|| {
1506            let (width, height) = (self.scene().width(), self.scene().height());
1507            let origin = self.sink_origin;
1508            let rects = IPlatformRenderInterfaceRegion::rects(Self::region_impl(region))
1509                .iter()
1510                .map(|rect| {
1511                    Self::whole_pixels(
1512                        kurbo::Rect::new(
1513                            rect.left as f64 - origin.x,
1514                            rect.top as f64 - origin.y,
1515                            rect.right as f64 - origin.x,
1516                            rect.bottom as f64 - origin.y,
1517                        ),
1518                        width,
1519                        height,
1520                    )
1521                })
1522                .collect::<Option<Vec<_>>>();
1523            ClipRecord {
1524                rects,
1525                pushed: self.backdrop.is_some().then(|| (path.clone(), Fill::NonZero, pixel_transform, anti_alias)),
1526            }
1527        });
1528
1529        self.scene().push_clip(&path, Fill::NonZero, pixel_transform, anti_alias);
1530        self.save_with(SavedKind::Clip, record);
1531    }
1532
1533    fn pop_clip(&mut self) {
1534        self.restore();
1535    }
1536
1537    fn push_layer(&mut self, _bounds: Rect) {
1538        // The bounds are what the caller draws in: a layer of the scene is
1539        // as large as what is drawn into it.
1540        self.sink().push_layer(BlendMode::default(), 1.0);
1541        self.save(SavedKind::Layer);
1542    }
1543
1544    fn pop_layer(&mut self) {
1545        self.restore();
1546    }
1547
1548    fn push_opacity(&mut self, opacity: f64, _bounds: Option<Rect>) {
1549        let use_opacity_layer = self.uses_opacity_layer();
1550        self.opacity_stack.push((self.current_opacity, use_opacity_layer));
1551
1552        if use_opacity_layer {
1553            // Take the current multiplied opacity.
1554            let opacity = self.current_opacity * opacity;
1555
1556            // Opacity is applied via layering.
1557            self.current_opacity = 1.0;
1558
1559            self.sink().push_layer(BlendMode::default(), opacity.clamp(0.0, 1.0) as f32);
1560            self.save(SavedKind::Layer);
1561        } else {
1562            self.current_opacity *= opacity;
1563        }
1564    }
1565
1566    fn pop_opacity(&mut self) {
1567        let (opacity, used_opacity_layer) =
1568            self.opacity_stack.pop().unwrap_or_else(|| panic!("The opacity stack is empty"));
1569
1570        if used_opacity_layer {
1571            self.restore();
1572        }
1573
1574        self.current_opacity = opacity;
1575    }
1576
1577    fn push_opacity_mask(&mut self, mask: &dyn IBrush, bounds: Rect) {
1578        self.sink().push_layer(BlendMode::default(), 1.0);
1579        self.save(SavedKind::Layer);
1580
1581        let transform = self.device_transform();
1582        let mask_paint = self.create_paint(mask, bounds);
1583        self.mask_stack.push((transform, mask_paint));
1584    }
1585
1586    fn pop_opacity_mask(&mut self) {
1587        let (transform, paint_wrapper) =
1588            self.mask_stack.pop().unwrap_or_else(|| panic!("The opacity mask stack is empty"));
1589
1590        // What was drawn since the push is kept where the mask is opaque:
1591        // the mask is drawn over all of it and composed "destination in".
1592        let target = self.target_path();
1593        let sink = self.sink();
1594        sink.push_layer(BlendMode::new(Mix::Normal, Compose::DestIn), 1.0);
1595
1596        if let Some(underlay) = paint_wrapper.underlay {
1597            sink.fill(&target, Fill::NonZero, Affine::IDENTITY, &VelloScenePaint::solid(underlay), BlendMode::default(), false);
1598        }
1599        if let Some(paint) = &paint_wrapper.paint {
1600            // The paint was made for the transform of the push; the path is
1601            // in pixels.
1602            let paint = VelloScenePaint { brush: paint.brush.clone(), transform: transform * paint.transform };
1603            match &paint_wrapper.clip {
1604                Some(clip) => {
1605                    sink.push_clip(clip, Fill::NonZero, transform, false);
1606                    sink.fill(&target, Fill::NonZero, Affine::IDENTITY, &paint, BlendMode::default(), false);
1607                    sink.pop_clip();
1608                }
1609                None => sink.fill(&target, Fill::NonZero, Affine::IDENTITY, &paint, BlendMode::default(), false),
1610            }
1611        }
1612
1613        sink.pop_layer();
1614        paint_wrapper.dispose();
1615
1616        self.restore();
1617    }
1618
1619    fn push_geometry_clip(&mut self, clip: &dyn IGeometryImpl) {
1620        match Self::geometry_impl(clip).fill_path() {
1621            Some(fill_path) => self.push_clip_path(fill_path.path(), to_fill(fill_path.fill_rule()), true),
1622            None => self.save(SavedKind::Nothing),
1623        }
1624    }
1625
1626    fn pop_geometry_clip(&mut self) {
1627        self.restore();
1628    }
1629
1630    fn push_render_options(&mut self, render_options: RenderOptions) {
1631        self.render_options_stack.push(self.render_options);
1632
1633        self.render_options = self.render_options.merge_with(render_options);
1634    }
1635
1636    fn pop_render_options(&mut self) {
1637        self.render_options =
1638            self.render_options_stack.pop().unwrap_or_else(|| panic!("The render options stack is empty"));
1639    }
1640
1641    fn push_text_options(&mut self, text_options: TextOptions) {
1642        self.text_options_stack.push(self.text_options);
1643
1644        self.text_options = self.text_options.merge_with(text_options);
1645    }
1646
1647    fn pop_text_options(&mut self) {
1648        self.text_options =
1649            self.text_options_stack.pop().unwrap_or_else(|| panic!("The text options stack is empty"));
1650    }
1651
1652    fn as_drawing_context_impl_with_effects(
1653        &mut self,
1654    ) -> Option<&mut dyn ferroui_base::platform::IDrawingContextImplWithEffects> {
1655        Some(self)
1656    }
1657
1658    fn as_drawing_context_with_acrylic_like_support(
1659        &mut self,
1660    ) -> Option<&mut dyn ferroui_base::platform::IDrawingContextWithAcrylicLikeSupport> {
1661        Some(self)
1662    }
1663
1664    fn get_feature(&mut self, _feature_type: TypeId) -> Option<Rc<dyn Any>> {
1665        None
1666    }
1667
1668    fn as_any_mut(&mut self) -> &mut dyn Any {
1669        self
1670    }
1671
1672    fn dispose(&mut self) {
1673        if self.disposed {
1674            return;
1675        }
1676        self.disposed = true;
1677
1678        // States the caller left pushed end with the frame, as the canvas of
1679        // Skia restores them.
1680        for (_, paint_wrapper) in std::mem::take(&mut self.mask_stack) {
1681            paint_wrapper.dispose();
1682        }
1683        while !self.state_stack.is_empty() {
1684            self.restore();
1685        }
1686
1687        // A target nothing was drawn to keeps what it held.
1688        if let Some(composes) = self.backdrop.as_ref().map(|backdrop| backdrop.composes_over_target) {
1689            if composes && self.scene().capabilities().retained_targets {
1690                self.backdrop = None;
1691                self.scene().retain_target(&[]);
1692            } else {
1693                self.start_with_backdrop();
1694            }
1695        }
1696
1697        // The recording of the scene: the life of the context up to here.
1698        // The contexts of intermediate surfaces that were drawn meanwhile
1699        // are counted in it too.
1700        if let Some(created) = self.created {
1701            crate::perf::add_time(crate::perf::Phase::Scene, created.elapsed());
1702        }
1703
1704        if let (Some(mut sink), Some(on_finished)) = (self.sink.take(), self.on_finished.take()) {
1705            on_finished(&mut *sink);
1706        }
1707
1708        for disposable in std::mem::take(&mut self.disposables) {
1709            disposable();
1710        }
1711    }
1712}
1713
1714/// A paint and what has to surround the drawing with it.
1715///
1716/// The paint wrapper of the Skia backend holds a paint of Skia, which can
1717/// compose shaders and sample an image as nothing beside itself; a paint of
1718/// a scene of this backend is one brush, so what such a paint of Skia does
1719/// in one is listed here and drawn in steps
1720/// ([`DrawingContextImpl::draw_with`]).
1721pub(crate) struct PaintWrapper {
1722    /// The paint, or `None` when the brush paints nothing.
1723    paint: Option<VelloScenePaint>,
1724    /// A color that is drawn before the paint, under it.
1725    underlay: Option<AlphaColor<Srgb>>,
1726    /// The opacity the underlay and the paint are composed with as one.
1727    layer_opacity: Option<f32>,
1728    /// What the paint is limited to, in the space of the shape.
1729    clip: Option<BezPath>,
1730}
1731
1732impl PaintWrapper {
1733    fn new() -> Self {
1734        Self { paint: None, underlay: None, layer_opacity: None, clip: None }
1735    }
1736
1737    /// Releases what the paint holds.
1738    fn dispose(self) {}
1739}