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
42pub struct CreateInfo {
44 pub sink: Box<dyn IVelloSceneSink>,
47
48 pub backdrop: Option<VelloBackdrop>,
51
52 pub on_finished: Box<dyn FnOnce(&mut dyn IVelloSceneSink)>,
55
56 pub scale_drawing_to_dpi: bool,
58
59 pub dpi: Vector,
61
62 pub rendering_modes: Vec<VelloRenderingMode>,
65}
66
67pub struct VelloBackdrop {
91 pub composes_over_target: bool,
96
97 pub brush: Box<dyn FnOnce() -> Option<VelloSceneBrush>>,
101}
102
103impl VelloBackdrop {
104 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
122pub(crate) enum IntermediateSurface {
128 Memory(SurfaceRenderTarget),
130 #[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 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 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
183struct ClipRecord {
185 rects: Option<Vec<VelloScenePixelRect>>,
189 pushed: Option<(BezPath, Fill, Affine, bool)>,
192}
193
194#[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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
206enum SavedKind {
207 Clip,
209 Layer,
211 Nothing,
213 Effect,
215}
216
217pub 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 intermediate_surface_dpi: Vector,
232 rendering_modes: Vec<VelloRenderingMode>,
233 state_stack: Vec<(Matrix, SavedKind)>,
234 clip_records: Vec<Option<ClipRecord>>,
237 backdrop: Option<VelloBackdrop>,
240 retains_target: bool,
242 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 effect_stack: Vec<effects::EffectFrame>,
257 open_clips: usize,
259 sink_origin: kurbo::Vec2,
263 created: Option<std::time::Instant>,
266}
267
268impl DrawingContextImpl {
269 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 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 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 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 fn start_with_backdrop(&mut self) {
349 let Some(backdrop) = self.backdrop.take() else {
350 return;
351 };
352
353 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 fn records_clips(&self) -> bool {
380 self.backdrop.is_some() || self.retains_target
381 }
382
383 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 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 pub fn rendering_mode(&mut self) -> VelloRenderingMode {
422 self.scene().rendering_mode()
423 }
424
425 #[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 pub fn render_options(&self) -> RenderOptions {
435 self.render_options
436 }
437
438 pub fn set_render_options(&mut self, value: RenderOptions) {
440 self.render_options = value;
441 }
442
443 pub fn text_options(&self) -> TextOptions {
445 self.text_options
446 }
447
448 pub fn set_text_options(&mut self, value: TextOptions) {
450 self.text_options = value;
451 }
452
453 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 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 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 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 fn save(&mut self, kind: SavedKind) {
496 self.save_with(kind, None);
497 }
498
499 fn save_with(&mut self, kind: SavedKind, record: Option<ClipRecord>) {
501 if kind == SavedKind::Clip {
502 self.open_clips += 1;
503 }
504 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 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 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 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 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 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 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 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 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 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 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 if radius_x != radius_y {
715 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 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 (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 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 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 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 return;
801 };
802
803 let transform = transform.map(to_affine).unwrap_or(Affine::IDENTITY);
804
805 match underlay {
806 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 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 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 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 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 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 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 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 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 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 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 fn create_stroke(&mut self, pen: &dyn IPen, target_rect: Rect) -> Option<(PaintWrapper, Stroke)> {
972 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 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 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 #[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 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 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 if !self.drawn {
1161 return;
1162 }
1163 }
1164 } else if self.backdrop.is_none() && !self.drawn {
1165 return;
1168 }
1169 }
1170
1171 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 #[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 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 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 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 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 if !glyph_run_impl.face().is_drawable() {
1382 return;
1383 }
1384
1385 let paint_wrapper = self.create_paint(foreground, glyph_run.bounds());
1386
1387 let mut effective_text_options = self.text_options;
1390
1391 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 let anti_alias = effective_text_options.text_rendering_mode != TextRenderingMode::Alias;
1409
1410 let hint = effective_text_options.text_hinting_mode != TextHintingMode::None;
1413
1414 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 #[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 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 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 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 let opacity = self.current_opacity * opacity;
1555
1556 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 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 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 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 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 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
1714pub(crate) struct PaintWrapper {
1722 paint: Option<VelloScenePaint>,
1724 underlay: Option<AlphaColor<Srgb>>,
1726 layer_opacity: Option<f32>,
1728 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 fn dispose(self) {}
1739}