1use crate::RenderMode;
7use crate::dispatch::Dispatcher;
8
9#[cfg(feature = "multithreading")]
10use crate::dispatch::multi_threaded::MultiThreadedDispatcher;
11use crate::dispatch::single_threaded::SingleThreadedDispatcher;
12use crate::kurbo::{PathEl, Point};
13use alloc::boxed::Box;
14#[cfg(feature = "text")]
15use alloc::sync::Arc;
16use alloc::vec;
17use alloc::vec::Vec;
18use vello_common::blurred_rounded_rect::BlurredRoundedRectangle;
19use vello_common::encode::{EncodeExt, EncodedPaint};
20use vello_common::fearless_simd::Level;
21use vello_common::kurbo::{Affine, BezPath, Cap, Join, Rect, Shape, Stroke};
22use vello_common::mask::Mask;
23#[cfg(feature = "text")]
24use vello_common::paint::ImageSource;
25use vello_common::paint::{Paint, PaintType};
26use vello_common::peniko::color::palette::css::BLACK;
27use vello_common::peniko::{BlendMode, Compose, Fill, Mix};
28use vello_common::pixmap::Pixmap;
29use vello_common::recording::{PushLayerCommand, Recordable, Recording, RenderCommand};
30use vello_common::strip::Strip;
31use vello_common::strip_generator::StripGenerator;
32#[cfg(feature = "text")]
33use vello_common::{
34 color::{AlphaColor, Srgb},
35 colr::{ColrPainter, ColrRenderer},
36 glyph::{GlyphRenderer, GlyphRunBuilder, GlyphType, PreparedGlyph},
37};
38
39pub(crate) const DEFAULT_TOLERANCE: f64 = 0.1;
40#[derive(Debug)]
42pub struct RenderContext {
43 pub(crate) width: u16,
44 pub(crate) height: u16,
45 pub(crate) paint: PaintType,
46 pub(crate) paint_transform: Affine,
47 pub(crate) stroke: Stroke,
48 pub(crate) transform: Affine,
49 pub(crate) fill_rule: Fill,
50 pub(crate) temp_path: BezPath,
51 pub(crate) anti_alias: bool,
53 pub(crate) encoded_paints: Vec<EncodedPaint>,
54 #[cfg_attr(
55 not(feature = "text"),
56 allow(dead_code, reason = "used when the `text` feature is enabled")
57 )]
58 pub(crate) render_settings: RenderSettings,
59 dispatcher: Box<dyn Dispatcher>,
60}
61
62#[derive(Copy, Clone, Debug)]
64pub struct RenderSettings {
65 pub level: Level,
67 pub num_threads: u16,
70 pub render_mode: RenderMode,
79}
80
81impl Default for RenderSettings {
82 fn default() -> Self {
83 Self {
84 level: Level::new(),
85 #[cfg(feature = "multithreading")]
86 num_threads: std::thread::available_parallelism()
87 .unwrap()
88 .get()
89 .saturating_sub(1) as u16,
90 #[cfg(not(feature = "multithreading"))]
91 num_threads: 0,
92 render_mode: RenderMode::OptimizeSpeed,
93 }
94 }
95}
96
97impl RenderContext {
98 pub fn new(width: u16, height: u16) -> Self {
100 Self::new_with(width, height, RenderSettings::default())
101 }
102
103 pub fn new_with(width: u16, height: u16, settings: RenderSettings) -> Self {
105 #[cfg(feature = "multithreading")]
106 let dispatcher: Box<dyn Dispatcher> = if settings.num_threads == 0 {
107 Box::new(SingleThreadedDispatcher::new(width, height, settings.level))
108 } else {
109 Box::new(MultiThreadedDispatcher::new(
110 width,
111 height,
112 settings.num_threads,
113 settings.level,
114 ))
115 };
116
117 #[cfg(not(feature = "multithreading"))]
118 let dispatcher: Box<dyn Dispatcher> =
119 { Box::new(SingleThreadedDispatcher::new(width, height, settings.level)) };
120
121 let transform = Affine::IDENTITY;
122 let fill_rule = Fill::NonZero;
123 let paint = BLACK.into();
124 let paint_transform = Affine::IDENTITY;
125 let stroke = Stroke {
126 width: 1.0,
127 join: Join::Bevel,
128 start_cap: Cap::Butt,
129 end_cap: Cap::Butt,
130 ..Default::default()
131 };
132 let encoded_paints = vec![];
133 let temp_path = BezPath::new();
134 let anti_alias = true;
135
136 Self {
137 width,
138 height,
139 dispatcher,
140 transform,
141 anti_alias,
142 paint,
143 render_settings: settings,
144 paint_transform,
145 fill_rule,
146 stroke,
147 temp_path,
148 encoded_paints,
149 }
150 }
151
152 fn encode_current_paint(&mut self) -> Paint {
153 match self.paint.clone() {
154 PaintType::Solid(s) => s.into(),
155 PaintType::Gradient(g) => {
156 g.encode_into(
158 &mut self.encoded_paints,
159 self.transform * self.paint_transform,
160 )
161 }
162 PaintType::Image(i) => i.encode_into(
163 &mut self.encoded_paints,
164 self.transform * self.paint_transform,
165 ),
166 }
167 }
168
169 pub fn fill_path(&mut self, path: &BezPath) {
171 let paint = self.encode_current_paint();
172 self.dispatcher
173 .fill_path(path, self.fill_rule, self.transform, paint, self.anti_alias);
174 }
175
176 pub fn stroke_path(&mut self, path: &BezPath) {
178 let paint = self.encode_current_paint();
179 self.dispatcher
180 .stroke_path(path, &self.stroke, self.transform, paint, self.anti_alias);
181 }
182
183 pub fn fill_rect(&mut self, rect: &Rect) {
185 self.temp_path.truncate(0);
190 self.temp_path
191 .push(PathEl::MoveTo(Point::new(rect.x0, rect.y0)));
192 self.temp_path
193 .push(PathEl::LineTo(Point::new(rect.x1, rect.y0)));
194 self.temp_path
195 .push(PathEl::LineTo(Point::new(rect.x1, rect.y1)));
196 self.temp_path
197 .push(PathEl::LineTo(Point::new(rect.x0, rect.y1)));
198 self.temp_path.push(PathEl::ClosePath);
199
200 let paint = self.encode_current_paint();
201 self.dispatcher.fill_path(
202 &self.temp_path,
203 self.fill_rule,
204 self.transform,
205 paint,
206 self.anti_alias,
207 );
208 }
209
210 pub fn fill_blurred_rounded_rect(&mut self, rect: &Rect, radius: f32, std_dev: f32) {
215 let color = match self.paint {
216 PaintType::Solid(s) => s,
217 _ => BLACK,
219 };
220
221 let blurred_rect = BlurredRoundedRectangle {
222 rect: *rect,
223 color,
224 radius,
225 std_dev,
226 };
227
228 let kernel_size = 2.5 * std_dev;
233 let inflated_rect = rect.inflate(f64::from(kernel_size), f64::from(kernel_size));
234 let transform = self.transform * self.paint_transform;
235
236 let paint = blurred_rect.encode_into(&mut self.encoded_paints, transform);
237 self.dispatcher.fill_path(
238 &inflated_rect.to_path(0.1),
239 Fill::NonZero,
240 self.transform,
241 paint,
242 self.anti_alias,
243 );
244 }
245
246 pub fn stroke_rect(&mut self, rect: &Rect) {
248 self.stroke_path(&rect.to_path(DEFAULT_TOLERANCE));
249 }
250
251 #[cfg(feature = "text")]
253 pub fn glyph_run(&mut self, font: &crate::peniko::Font) -> GlyphRunBuilder<'_, Self> {
254 GlyphRunBuilder::new(font.clone(), self.transform, self)
255 }
256
257 pub fn push_layer(
263 &mut self,
264 clip_path: Option<&BezPath>,
265 blend_mode: Option<BlendMode>,
266 opacity: Option<f32>,
267 mask: Option<Mask>,
268 ) {
269 let mask = mask.and_then(|m| {
270 if m.width() != self.width || m.height() != self.height {
271 None
272 } else {
273 Some(m)
274 }
275 });
276
277 let blend_mode = blend_mode.unwrap_or(BlendMode::new(Mix::Normal, Compose::SrcOver));
278 let opacity = opacity.unwrap_or(1.0);
279
280 self.dispatcher.push_layer(
281 clip_path,
282 self.fill_rule,
283 self.transform,
284 blend_mode,
285 opacity,
286 self.anti_alias,
287 mask,
288 );
289 }
290
291 pub fn push_clip_layer(&mut self, path: &BezPath) {
293 self.push_layer(Some(path), None, None, None);
294 }
295
296 pub fn push_blend_layer(&mut self, blend_mode: BlendMode) {
298 self.push_layer(None, Some(blend_mode), None, None);
299 }
300
301 pub fn push_opacity_layer(&mut self, opacity: f32) {
303 self.push_layer(None, None, Some(opacity), None);
304 }
305
306 pub fn set_anti_aliasing(&mut self, value: bool) {
308 self.anti_alias = value;
309 }
310
311 pub fn push_mask_layer(&mut self, mask: Mask) {
317 self.push_layer(None, None, None, Some(mask));
318 }
319
320 pub fn pop_layer(&mut self) {
322 self.dispatcher.pop_layer();
323 }
324
325 pub fn set_stroke(&mut self, stroke: Stroke) {
327 self.stroke = stroke;
328 }
329
330 pub fn stroke(&self) -> &Stroke {
332 &self.stroke
333 }
334
335 pub fn set_paint(&mut self, paint: impl Into<PaintType>) {
337 self.paint = paint.into();
338 }
339
340 pub fn paint(&self) -> &PaintType {
342 &self.paint
343 }
344
345 pub fn set_paint_transform(&mut self, paint_transform: Affine) {
351 self.paint_transform = paint_transform;
352 }
353
354 pub fn paint_transform(&self) -> &Affine {
356 &self.paint_transform
357 }
358
359 pub fn reset_paint_transform(&mut self) {
361 self.paint_transform = Affine::IDENTITY;
362 }
363
364 pub fn set_fill_rule(&mut self, fill_rule: Fill) {
366 self.fill_rule = fill_rule;
367 }
368
369 pub fn fill_rule(&self) -> &Fill {
371 &self.fill_rule
372 }
373
374 pub fn set_transform(&mut self, transform: Affine) {
376 self.transform = transform;
377 }
378
379 pub fn transform(&self) -> &Affine {
381 &self.transform
382 }
383
384 pub fn reset_transform(&mut self) {
386 self.transform = Affine::IDENTITY;
387 }
388
389 pub fn reset(&mut self) {
391 self.dispatcher.reset();
392 self.encoded_paints.clear();
393 self.reset_transform();
394 self.reset_paint_transform();
395 }
396
397 pub fn flush(&mut self) {
403 self.dispatcher.flush();
404 }
405
406 pub fn render_to_buffer(
409 &self,
410 buffer: &mut [u8],
411 width: u16,
412 height: u16,
413 render_mode: RenderMode,
414 ) {
415 let wide = self.dispatcher.wide();
417 assert!(!wide.has_layers(), "some layers haven't been popped yet");
418 assert_eq!(
419 buffer.len(),
420 (width as usize) * (height as usize) * 4,
421 "provided width ({}) and height ({}) do not match buffer size ({})",
422 width,
423 height,
424 buffer.len(),
425 );
426
427 self.dispatcher
428 .rasterize(buffer, render_mode, width, height, &self.encoded_paints);
429 }
430
431 pub fn render_to_pixmap(&self, pixmap: &mut Pixmap) {
433 let width = pixmap.width();
434 let height = pixmap.height();
435 self.render_to_buffer(
436 pixmap.data_as_u8_slice_mut(),
437 width,
438 height,
439 self.render_settings.render_mode,
440 );
441 }
442
443 pub fn width(&self) -> u16 {
445 self.width
446 }
447
448 pub fn height(&self) -> u16 {
450 self.height
451 }
452
453 pub fn render_settings(&self) -> &RenderSettings {
455 &self.render_settings
456 }
457}
458
459#[cfg(feature = "text")]
460impl GlyphRenderer for RenderContext {
461 fn fill_glyph(&mut self, prepared_glyph: PreparedGlyph<'_>) {
462 match prepared_glyph.glyph_type {
463 GlyphType::Outline(glyph) => {
464 let paint = self.encode_current_paint();
465 self.dispatcher.fill_path(
466 glyph.path,
467 Fill::NonZero,
468 prepared_glyph.transform,
469 paint,
470 self.anti_alias,
471 );
472 }
473 GlyphType::Bitmap(glyph) => {
474 let old_transform = self.transform;
478 let old_paint = self.paint.clone();
479
480 let quality = if prepared_glyph.transform.as_coeffs()[0] < 0.5
482 || prepared_glyph.transform.as_coeffs()[3] < 0.5
483 {
484 crate::peniko::ImageQuality::High
485 } else {
486 crate::peniko::ImageQuality::Medium
487 };
488
489 let image = vello_common::paint::Image {
490 source: ImageSource::Pixmap(Arc::new(glyph.pixmap)),
491 x_extend: crate::peniko::Extend::Pad,
492 y_extend: crate::peniko::Extend::Pad,
493 quality,
494 };
495
496 self.set_paint(image);
497 self.set_transform(prepared_glyph.transform);
498 self.fill_rect(&glyph.area);
499
500 self.set_paint(old_paint);
502 self.transform = old_transform;
503 }
504 GlyphType::Colr(glyph) => {
505 let old_transform = self.transform;
507 let old_paint = self.paint.clone();
508 let context_color = match old_paint {
509 PaintType::Solid(s) => s,
510 _ => BLACK,
511 };
512
513 let area = glyph.area;
514
515 let glyph_pixmap = {
516 let settings = RenderSettings {
517 level: self.render_settings.level,
518 render_mode: self.render_settings.render_mode,
519 num_threads: 0,
520 };
521
522 let mut ctx = Self::new_with(glyph.pix_width, glyph.pix_height, settings);
523 let mut pix = Pixmap::new(glyph.pix_width, glyph.pix_height);
524
525 let mut colr_painter = ColrPainter::new(glyph, context_color, &mut ctx);
526 colr_painter.paint();
527
528 ctx.flush();
531 ctx.render_to_pixmap(&mut pix);
532
533 pix
534 };
535
536 let image = vello_common::paint::Image {
537 source: ImageSource::Pixmap(Arc::new(glyph_pixmap)),
538 x_extend: crate::peniko::Extend::Pad,
539 y_extend: crate::peniko::Extend::Pad,
540 quality: crate::peniko::ImageQuality::Low,
543 };
544
545 self.set_paint(image);
546 self.set_transform(prepared_glyph.transform);
547 self.fill_rect(&area);
548
549 self.set_paint(old_paint);
551 self.transform = old_transform;
552 }
553 }
554 }
555
556 fn stroke_glyph(&mut self, prepared_glyph: PreparedGlyph<'_>) {
557 match prepared_glyph.glyph_type {
558 GlyphType::Outline(glyph) => {
559 let paint = self.encode_current_paint();
560 self.dispatcher.stroke_path(
561 glyph.path,
562 &self.stroke,
563 prepared_glyph.transform,
564 paint,
565 self.anti_alias,
566 );
567 }
568 GlyphType::Bitmap(_) | GlyphType::Colr(_) => {
569 self.fill_glyph(prepared_glyph);
572 }
573 }
574 }
575}
576
577#[cfg(feature = "text")]
578impl ColrRenderer for RenderContext {
579 fn push_clip_layer(&mut self, clip: &BezPath) {
580 Self::push_clip_layer(self, clip);
581 }
582
583 fn push_blend_layer(&mut self, blend_mode: BlendMode) {
584 Self::push_blend_layer(self, blend_mode);
585 }
586
587 fn fill_solid(&mut self, color: AlphaColor<Srgb>) {
588 self.set_paint(color);
589 self.fill_rect(&Rect::new(
590 0.0,
591 0.0,
592 f64::from(self.width),
593 f64::from(self.height),
594 ));
595 }
596
597 fn fill_gradient(&mut self, gradient: crate::peniko::Gradient) {
598 self.set_paint(gradient);
599 self.fill_rect(&Rect::new(
600 0.0,
601 0.0,
602 f64::from(self.width),
603 f64::from(self.height),
604 ));
605 }
606
607 fn set_paint_transform(&mut self, affine: Affine) {
608 Self::set_paint_transform(self, affine);
609 }
610
611 fn pop_layer(&mut self) {
612 Self::pop_layer(self);
613 }
614}
615
616impl Recordable for RenderContext {
617 fn prepare_recording(&mut self, recording: &mut Recording) {
618 let buffers = recording.take_cached_strips();
619 let (strips, alphas, strip_start_indices) =
620 self.generate_strips_from_commands(recording.commands(), buffers);
621 recording.set_cached_strips(strips, alphas, strip_start_indices);
622 }
623
624 fn execute_recording(&mut self, recording: &Recording) {
625 let (cached_strips, cached_alphas) = recording.get_cached_strips();
626 let adjusted_strips = self.prepare_cached_strips(cached_strips, cached_alphas);
627
628 let strip_start_indices = recording.get_strip_start_indices();
630 let mut range_index = 0;
631
632 for command in recording.commands() {
634 match command {
635 RenderCommand::FillPath(_)
636 | RenderCommand::StrokePath(_)
637 | RenderCommand::FillRect(_)
638 | RenderCommand::StrokeRect(_) => {
639 self.process_geometry_command(
640 command,
641 strip_start_indices,
642 range_index,
643 &adjusted_strips,
644 );
645 range_index += 1;
646 }
647 #[cfg(feature = "text")]
648 RenderCommand::FillOutlineGlyph(_) | RenderCommand::StrokeOutlineGlyph(_) => {
649 self.process_geometry_command(
650 command,
651 strip_start_indices,
652 range_index,
653 &adjusted_strips,
654 );
655 range_index += 1;
656 }
657 RenderCommand::SetPaint(paint) => {
658 self.set_paint(paint.clone());
659 }
660 RenderCommand::SetPaintTransform(transform) => {
661 self.set_paint_transform(*transform);
662 }
663 RenderCommand::ResetPaintTransform => {
664 self.reset_paint_transform();
665 }
666 RenderCommand::SetTransform(transform) => {
667 self.set_transform(*transform);
668 }
669 RenderCommand::SetFillRule(fill_rule) => {
670 self.set_fill_rule(*fill_rule);
671 }
672 RenderCommand::SetStroke(stroke) => {
673 self.set_stroke(stroke.clone());
674 }
675 RenderCommand::PushLayer(PushLayerCommand {
676 clip_path,
677 blend_mode,
678 opacity,
679 mask,
680 }) => {
681 self.push_layer(clip_path.as_ref(), *blend_mode, *opacity, mask.clone());
682 }
683 RenderCommand::PopLayer => {
684 self.pop_layer();
685 }
686 }
687 }
688 }
689}
690
691#[derive(Debug)]
693struct RenderState {
694 transform: Affine,
695 fill_rule: Fill,
696 stroke: Stroke,
697 paint: PaintType,
698 paint_transform: Affine,
699 alphas: Vec<u8>,
700}
701
702impl RenderContext {
704 fn generate_strips_from_commands(
711 &mut self,
712 commands: &[RenderCommand],
713 buffers: (Vec<Strip>, Vec<u8>, Vec<usize>),
714 ) -> (Vec<Strip>, Vec<u8>, Vec<usize>) {
715 let (mut collected_strips, mut cached_alphas, mut strip_start_indices) = buffers;
716 collected_strips.clear();
717 cached_alphas.clear();
718 strip_start_indices.clear();
719
720 let saved_state = self.take_current_state(cached_alphas);
721 let mut strip_generator =
722 StripGenerator::new(self.width, self.height, self.render_settings.level);
723
724 for command in commands {
725 let start_index = collected_strips.len();
726
727 match command {
728 RenderCommand::FillPath(path) => {
729 self.generate_fill_strips(
730 path,
731 &mut collected_strips,
732 self.transform,
733 &mut strip_generator,
734 );
735 strip_start_indices.push(start_index);
736 }
737 RenderCommand::StrokePath(path) => {
738 self.generate_stroke_strips(
739 path,
740 &mut collected_strips,
741 self.transform,
742 &mut strip_generator,
743 );
744 strip_start_indices.push(start_index);
745 }
746 RenderCommand::FillRect(rect) => {
747 let path = rect.to_path(DEFAULT_TOLERANCE);
748 self.generate_fill_strips(
749 &path,
750 &mut collected_strips,
751 self.transform,
752 &mut strip_generator,
753 );
754 strip_start_indices.push(start_index);
755 }
756 RenderCommand::StrokeRect(rect) => {
757 let path = rect.to_path(DEFAULT_TOLERANCE);
758 self.generate_stroke_strips(
759 &path,
760 &mut collected_strips,
761 self.transform,
762 &mut strip_generator,
763 );
764 strip_start_indices.push(start_index);
765 }
766 #[cfg(feature = "text")]
767 RenderCommand::FillOutlineGlyph((path, transform)) => {
768 let glyph_transform = self.transform * *transform;
769 self.generate_fill_strips(
770 path,
771 &mut collected_strips,
772 glyph_transform,
773 &mut strip_generator,
774 );
775 strip_start_indices.push(start_index);
776 }
777 #[cfg(feature = "text")]
778 RenderCommand::StrokeOutlineGlyph((path, transform)) => {
779 let glyph_transform = self.transform * *transform;
780 self.generate_stroke_strips(
781 path,
782 &mut collected_strips,
783 glyph_transform,
784 &mut strip_generator,
785 );
786 strip_start_indices.push(start_index);
787 }
788 RenderCommand::SetTransform(transform) => {
789 self.transform = *transform;
790 }
791 RenderCommand::SetFillRule(fill_rule) => {
792 self.fill_rule = *fill_rule;
793 }
794 RenderCommand::SetStroke(stroke) => {
795 self.stroke = stroke.clone();
796 }
797
798 _ => {}
799 }
800 }
801
802 let collected_alphas = strip_generator.take_alpha_buf();
803 self.restore_state(saved_state);
804
805 (collected_strips, collected_alphas, strip_start_indices)
806 }
807}
808
809impl RenderContext {
811 fn process_geometry_command(
812 &mut self,
813 command: &RenderCommand,
814 strip_start_indices: &[usize],
815 range_index: usize,
816 adjusted_strips: &[Strip],
817 ) {
818 assert!(
819 range_index < strip_start_indices.len(),
820 "Strip range index out of bounds"
821 );
822 let start = strip_start_indices[range_index];
823 let end = strip_start_indices
824 .get(range_index + 1)
825 .copied()
826 .unwrap_or(adjusted_strips.len());
827 let count = end - start;
828 assert!(
829 start < adjusted_strips.len() && count > 0,
830 "Invalid strip range"
831 );
832 let paint = self.encode_current_paint();
833 let fill_rule = match command {
834 RenderCommand::FillPath(_) | RenderCommand::FillRect(_) => self.fill_rule,
835 RenderCommand::StrokePath(_) | RenderCommand::StrokeRect(_) => Fill::NonZero,
836 _ => Fill::NonZero,
837 };
838 self.dispatcher
839 .wide_mut()
840 .generate(&adjusted_strips[start..end], fill_rule, paint, 0);
841 }
842
843 fn prepare_cached_strips(
845 &mut self,
846 cached_strips: &[Strip],
847 cached_alphas: &[u8],
848 ) -> Vec<Strip> {
849 let alpha_offset = self.dispatcher.alpha_buf().len() as u32;
851 self.dispatcher.extend_alpha_buf(cached_alphas);
853 cached_strips
855 .iter()
856 .map(move |strip| {
857 let mut adjusted_strip = *strip;
858 adjusted_strip.alpha_idx += alpha_offset;
859 adjusted_strip
860 })
861 .collect()
862 }
863
864 fn generate_fill_strips(
866 &mut self,
867 path: &BezPath,
868 strips: &mut Vec<Strip>,
869 transform: Affine,
870 strip_generator: &mut StripGenerator,
871 ) {
872 strip_generator.generate_filled_path(
873 path,
874 self.fill_rule,
875 transform,
876 self.anti_alias,
877 |generated_strips| {
878 strips.extend_from_slice(generated_strips);
879 },
880 );
881 }
882
883 fn generate_stroke_strips(
885 &mut self,
886 path: &BezPath,
887 strips: &mut Vec<Strip>,
888 transform: Affine,
889 strip_generator: &mut StripGenerator,
890 ) {
891 strip_generator.generate_stroked_path(
892 path,
893 &self.stroke,
894 transform,
895 self.anti_alias,
896 |generated_strips| {
897 strips.extend_from_slice(generated_strips);
898 },
899 );
900 }
901
902 fn take_current_state(&mut self, alphas: Vec<u8>) -> RenderState {
904 RenderState {
905 paint: self.paint.clone(),
906 paint_transform: self.paint_transform,
907 transform: self.transform,
908 fill_rule: self.fill_rule,
909 stroke: core::mem::take(&mut self.stroke),
910 alphas: self.dispatcher.replace_alpha_buf(alphas),
911 }
912 }
913
914 fn restore_state(&mut self, state: RenderState) {
916 self.transform = state.transform;
917 self.fill_rule = state.fill_rule;
918 self.stroke = state.stroke;
919 self.paint = state.paint;
920 self.paint_transform = state.paint_transform;
921 self.dispatcher.set_alpha_buf(state.alphas);
922 }
923}
924
925#[cfg(test)]
926mod tests {
927 use crate::RenderContext;
928 use vello_common::kurbo::{Rect, Shape};
929 use vello_common::tile::Tile;
930
931 #[test]
932 fn clip_overflow() {
933 let mut ctx = RenderContext::new(100, 100);
934
935 for _ in 0..(usize::from(u16::MAX) + 1).div_ceil(usize::from(Tile::HEIGHT * Tile::WIDTH)) {
936 ctx.fill_rect(&Rect::new(0.0, 0.0, 1.0, 1.0));
937 }
938
939 ctx.push_clip_layer(&Rect::new(20.0, 20.0, 180.0, 180.0).to_path(0.1));
940 ctx.pop_layer();
941 ctx.flush();
942 }
943
944 #[cfg(feature = "multithreading")]
945 #[test]
946 fn multithreaded_crash_after_reset() {
947 use crate::{Level, RenderMode, RenderSettings};
948 use vello_common::pixmap::Pixmap;
949
950 let mut pixmap = Pixmap::new(200, 200);
951 let settings = RenderSettings {
952 level: Level::new(),
953 num_threads: 1,
954 render_mode: RenderMode::OptimizeQuality,
955 };
956
957 let mut ctx = RenderContext::new_with(200, 200, settings);
958 ctx.reset();
959 ctx.fill_path(&Rect::new(0.0, 0.0, 100.0, 100.0).to_path(0.1));
960 ctx.flush();
961 ctx.render_to_pixmap(&mut pixmap);
962 ctx.flush();
963 ctx.render_to_pixmap(&mut pixmap);
964 }
965}