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, 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, Recorder, Recording, RenderCommand};
30use vello_common::strip::Strip;
31use vello_common::strip_generator::{GenerationMode, StripGenerator, StripStorage};
32#[cfg(feature = "text")]
33use vello_common::{
34 color::{AlphaColor, Srgb},
35 colr::{ColrPainter, ColrRenderer},
36 glyph::{GlyphRenderer, GlyphRunBuilder, GlyphType, PreparedGlyph},
37};
38
39#[derive(Debug)]
41pub struct RenderContext {
42 pub(crate) width: u16,
43 pub(crate) height: u16,
44 pub(crate) paint: PaintType,
45 pub(crate) paint_transform: Affine,
46 pub(crate) stroke: Stroke,
47 pub(crate) transform: Affine,
48 pub(crate) fill_rule: Fill,
49 pub(crate) temp_path: BezPath,
50 pub(crate) aliasing_threshold: Option<u8>,
51 pub(crate) encoded_paints: Vec<EncodedPaint>,
52 #[cfg_attr(
53 not(feature = "text"),
54 allow(dead_code, reason = "used when the `text` feature is enabled")
55 )]
56 pub(crate) render_settings: RenderSettings,
57 dispatcher: Box<dyn Dispatcher>,
58 #[cfg(feature = "text")]
59 pub(crate) glyph_caches: Option<vello_common::glyph::GlyphCaches>,
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::try_detect().unwrap_or(Level::fallback()),
85 #[cfg(feature = "multithreading")]
86 num_threads: (std::thread::available_parallelism()
87 .unwrap()
88 .get()
89 .saturating_sub(1) as u16)
90 .min(8),
91 #[cfg(not(feature = "multithreading"))]
92 num_threads: 0,
93 render_mode: RenderMode::OptimizeSpeed,
94 }
95 }
96}
97
98impl RenderContext {
99 pub fn new(width: u16, height: u16) -> Self {
101 Self::new_with(width, height, RenderSettings::default())
102 }
103
104 pub fn new_with(width: u16, height: u16, settings: RenderSettings) -> Self {
106 #[cfg(feature = "multithreading")]
107 let dispatcher: Box<dyn Dispatcher> = if settings.num_threads == 0 {
108 Box::new(SingleThreadedDispatcher::new(width, height, settings.level))
109 } else {
110 Box::new(MultiThreadedDispatcher::new(
111 width,
112 height,
113 settings.num_threads,
114 settings.level,
115 ))
116 };
117
118 #[cfg(not(feature = "multithreading"))]
119 let dispatcher: Box<dyn Dispatcher> =
120 { Box::new(SingleThreadedDispatcher::new(width, height, settings.level)) };
121
122 let transform = Affine::IDENTITY;
123 let fill_rule = Fill::NonZero;
124 let paint = BLACK.into();
125 let paint_transform = Affine::IDENTITY;
126 let stroke = Stroke {
127 width: 1.0,
128 join: Join::Bevel,
129 start_cap: Cap::Butt,
130 end_cap: Cap::Butt,
131 ..Default::default()
132 };
133 let encoded_paints = vec![];
134 let temp_path = BezPath::new();
135 let aliasing_threshold = None;
136
137 Self {
138 width,
139 height,
140 dispatcher,
141 transform,
142 aliasing_threshold,
143 paint,
144 render_settings: settings,
145 paint_transform,
146 fill_rule,
147 stroke,
148 temp_path,
149 encoded_paints,
150 #[cfg(feature = "text")]
151 glyph_caches: Some(Default::default()),
152 }
153 }
154
155 fn encode_current_paint(&mut self) -> Paint {
156 match self.paint.clone() {
157 PaintType::Solid(s) => s.into(),
158 PaintType::Gradient(g) => {
159 g.encode_into(
161 &mut self.encoded_paints,
162 self.transform * self.paint_transform,
163 )
164 }
165 PaintType::Image(i) => i.encode_into(
166 &mut self.encoded_paints,
167 self.transform * self.paint_transform,
168 ),
169 }
170 }
171
172 pub fn fill_path(&mut self, path: &BezPath) {
174 let paint = self.encode_current_paint();
175 self.dispatcher.fill_path(
176 path,
177 self.fill_rule,
178 self.transform,
179 paint,
180 self.aliasing_threshold,
181 );
182 }
183
184 pub fn stroke_path(&mut self, path: &BezPath) {
186 let paint = self.encode_current_paint();
187 self.dispatcher.stroke_path(
188 path,
189 &self.stroke,
190 self.transform,
191 paint,
192 self.aliasing_threshold,
193 );
194 }
195
196 pub fn fill_rect(&mut self, rect: &Rect) {
198 self.rect_to_temp_path(rect);
199 let paint = self.encode_current_paint();
200 self.dispatcher.fill_path(
201 &self.temp_path,
202 self.fill_rule,
203 self.transform,
204 paint,
205 self.aliasing_threshold,
206 );
207 }
208
209 fn rect_to_temp_path(&mut self, rect: &Rect) {
210 self.temp_path.truncate(0);
211 self.temp_path
212 .push(PathEl::MoveTo(Point::new(rect.x0, rect.y0)));
213 self.temp_path
214 .push(PathEl::LineTo(Point::new(rect.x1, rect.y0)));
215 self.temp_path
216 .push(PathEl::LineTo(Point::new(rect.x1, rect.y1)));
217 self.temp_path
218 .push(PathEl::LineTo(Point::new(rect.x0, rect.y1)));
219 self.temp_path.push(PathEl::ClosePath);
220 }
221
222 pub fn fill_blurred_rounded_rect(&mut self, rect: &Rect, radius: f32, std_dev: f32) {
227 let color = match self.paint {
228 PaintType::Solid(s) => s,
229 _ => BLACK,
231 };
232
233 let blurred_rect = BlurredRoundedRectangle {
234 rect: *rect,
235 color,
236 radius,
237 std_dev,
238 };
239
240 let kernel_size = 2.5 * std_dev;
245 let inflated_rect = rect.inflate(f64::from(kernel_size), f64::from(kernel_size));
246 let transform = self.transform * self.paint_transform;
247
248 self.rect_to_temp_path(&inflated_rect);
249
250 let paint = blurred_rect.encode_into(&mut self.encoded_paints, transform);
251 self.dispatcher.fill_path(
252 &self.temp_path,
253 Fill::NonZero,
254 self.transform,
255 paint,
256 self.aliasing_threshold,
257 );
258 }
259
260 pub fn stroke_rect(&mut self, rect: &Rect) {
262 self.rect_to_temp_path(rect);
263 let paint = self.encode_current_paint();
264 self.dispatcher.stroke_path(
265 &self.temp_path,
266 &self.stroke,
267 self.transform,
268 paint,
269 self.aliasing_threshold,
270 );
271 }
272
273 #[cfg(feature = "text")]
275 pub fn glyph_run(&mut self, font: &crate::peniko::FontData) -> GlyphRunBuilder<'_, Self> {
276 GlyphRunBuilder::new(font.clone(), self.transform, self)
277 }
278
279 pub fn push_layer(
285 &mut self,
286 clip_path: Option<&BezPath>,
287 blend_mode: Option<BlendMode>,
288 opacity: Option<f32>,
289 mask: Option<Mask>,
290 ) {
291 let mask = mask.and_then(|m| {
292 if m.width() != self.width || m.height() != self.height {
293 None
294 } else {
295 Some(m)
296 }
297 });
298
299 let blend_mode = blend_mode.unwrap_or(BlendMode::new(Mix::Normal, Compose::SrcOver));
300 let opacity = opacity.unwrap_or(1.0);
301
302 self.dispatcher.push_layer(
303 clip_path,
304 self.fill_rule,
305 self.transform,
306 blend_mode,
307 opacity,
308 self.aliasing_threshold,
309 mask,
310 );
311 }
312
313 pub fn push_clip_layer(&mut self, path: &BezPath) {
315 self.push_layer(Some(path), None, None, None);
316 }
317
318 pub fn push_blend_layer(&mut self, blend_mode: BlendMode) {
320 self.push_layer(None, Some(blend_mode), None, None);
321 }
322
323 pub fn push_opacity_layer(&mut self, opacity: f32) {
325 self.push_layer(None, None, Some(opacity), None);
326 }
327
328 pub fn set_aliasing_threshold(&mut self, aliasing_threshold: Option<u8>) {
340 self.aliasing_threshold = aliasing_threshold;
341 }
342
343 pub fn push_mask_layer(&mut self, mask: Mask) {
349 self.push_layer(None, None, None, Some(mask));
350 }
351
352 pub fn pop_layer(&mut self) {
354 self.dispatcher.pop_layer();
355 }
356
357 pub fn set_stroke(&mut self, stroke: Stroke) {
359 self.stroke = stroke;
360 }
361
362 pub fn stroke(&self) -> &Stroke {
364 &self.stroke
365 }
366
367 pub fn set_paint(&mut self, paint: impl Into<PaintType>) {
369 self.paint = paint.into();
370 }
371
372 pub fn paint(&self) -> &PaintType {
374 &self.paint
375 }
376
377 pub fn set_paint_transform(&mut self, paint_transform: Affine) {
383 self.paint_transform = paint_transform;
384 }
385
386 pub fn paint_transform(&self) -> &Affine {
388 &self.paint_transform
389 }
390
391 pub fn reset_paint_transform(&mut self) {
393 self.paint_transform = Affine::IDENTITY;
394 }
395
396 pub fn set_fill_rule(&mut self, fill_rule: Fill) {
398 self.fill_rule = fill_rule;
399 }
400
401 pub fn fill_rule(&self) -> &Fill {
403 &self.fill_rule
404 }
405
406 pub fn set_transform(&mut self, transform: Affine) {
408 self.transform = transform;
409 }
410
411 pub fn transform(&self) -> &Affine {
413 &self.transform
414 }
415
416 pub fn reset_transform(&mut self) {
418 self.transform = Affine::IDENTITY;
419 }
420
421 pub fn reset(&mut self) {
423 self.dispatcher.reset();
424 self.encoded_paints.clear();
425 self.reset_transform();
426 self.reset_paint_transform();
427 #[cfg(feature = "text")]
428 self.glyph_caches.as_mut().unwrap().maintain();
429 }
430
431 pub fn flush(&mut self) {
437 self.dispatcher.flush();
438 }
439
440 pub fn render_to_buffer(
443 &self,
444 buffer: &mut [u8],
445 width: u16,
446 height: u16,
447 render_mode: RenderMode,
448 ) {
449 let wide = self.dispatcher.wide();
451 assert!(!wide.has_layers(), "some layers haven't been popped yet");
452 assert_eq!(
453 buffer.len(),
454 (width as usize) * (height as usize) * 4,
455 "provided width ({}) and height ({}) do not match buffer size ({})",
456 width,
457 height,
458 buffer.len(),
459 );
460
461 self.dispatcher
462 .rasterize(buffer, render_mode, width, height, &self.encoded_paints);
463 }
464
465 pub fn render_to_pixmap(&self, pixmap: &mut Pixmap) {
467 let width = pixmap.width();
468 let height = pixmap.height();
469 self.render_to_buffer(
470 pixmap.data_as_u8_slice_mut(),
471 width,
472 height,
473 self.render_settings.render_mode,
474 );
475 }
476
477 pub fn width(&self) -> u16 {
479 self.width
480 }
481
482 pub fn height(&self) -> u16 {
484 self.height
485 }
486
487 pub fn render_settings(&self) -> &RenderSettings {
489 &self.render_settings
490 }
491}
492
493#[cfg(feature = "text")]
494impl GlyphRenderer for RenderContext {
495 fn fill_glyph(&mut self, prepared_glyph: PreparedGlyph<'_>) {
496 match prepared_glyph.glyph_type {
497 GlyphType::Outline(glyph) => {
498 let paint = self.encode_current_paint();
499 self.dispatcher.fill_path(
500 glyph.path,
501 Fill::NonZero,
502 prepared_glyph.transform,
503 paint,
504 self.aliasing_threshold,
505 );
506 }
507 GlyphType::Bitmap(glyph) => {
508 use vello_common::peniko::ImageSampler;
513 let old_transform = self.transform;
514 let old_paint = self.paint.clone();
515
516 let quality = if prepared_glyph.transform.as_coeffs()[0] < 0.5
518 || prepared_glyph.transform.as_coeffs()[3] < 0.5
519 {
520 crate::peniko::ImageQuality::High
521 } else {
522 crate::peniko::ImageQuality::Medium
523 };
524
525 let image = vello_common::paint::Image {
526 image: ImageSource::Pixmap(Arc::new(glyph.pixmap)),
527 sampler: ImageSampler {
528 x_extend: crate::peniko::Extend::Pad,
529 y_extend: crate::peniko::Extend::Pad,
530 quality,
531 alpha: 1.0,
532 },
533 };
534
535 self.set_paint(image);
536 self.set_transform(prepared_glyph.transform);
537 self.fill_rect(&glyph.area);
538
539 self.set_paint(old_paint);
541 self.transform = old_transform;
542 }
543 GlyphType::Colr(glyph) => {
544 use vello_common::peniko::ImageSampler;
547 let old_transform = self.transform;
548 let old_paint = self.paint.clone();
549 let context_color = match old_paint {
550 PaintType::Solid(s) => s,
551 _ => BLACK,
552 };
553
554 let area = glyph.area;
555
556 let glyph_pixmap = {
557 let settings = RenderSettings {
558 level: self.render_settings.level,
559 render_mode: self.render_settings.render_mode,
560 num_threads: 0,
561 };
562
563 let mut ctx = Self::new_with(glyph.pix_width, glyph.pix_height, settings);
564 let mut pix = Pixmap::new(glyph.pix_width, glyph.pix_height);
565
566 let mut colr_painter = ColrPainter::new(glyph, context_color, &mut ctx);
567 colr_painter.paint();
568
569 ctx.flush();
572 ctx.render_to_pixmap(&mut pix);
573
574 pix
575 };
576
577 let image = vello_common::paint::Image {
578 image: ImageSource::Pixmap(Arc::new(glyph_pixmap)),
579 sampler: ImageSampler {
580 x_extend: crate::peniko::Extend::Pad,
581 y_extend: crate::peniko::Extend::Pad,
582 quality: crate::peniko::ImageQuality::Low,
585 alpha: 1.0,
586 },
587 };
588
589 self.set_paint(image);
590 self.set_transform(prepared_glyph.transform);
591 self.fill_rect(&area);
592
593 self.set_paint(old_paint);
595 self.transform = old_transform;
596 }
597 }
598 }
599
600 fn stroke_glyph(&mut self, prepared_glyph: PreparedGlyph<'_>) {
601 match prepared_glyph.glyph_type {
602 GlyphType::Outline(glyph) => {
603 let paint = self.encode_current_paint();
604 self.dispatcher.stroke_path(
605 glyph.path,
606 &self.stroke,
607 prepared_glyph.transform,
608 paint,
609 self.aliasing_threshold,
610 );
611 }
612 GlyphType::Bitmap(_) | GlyphType::Colr(_) => {
613 self.fill_glyph(prepared_glyph);
616 }
617 }
618 }
619
620 fn take_glyph_caches(&mut self) -> vello_common::glyph::GlyphCaches {
621 self.glyph_caches.take().unwrap()
622 }
623
624 fn restore_glyph_caches(&mut self, cache: vello_common::glyph::GlyphCaches) {
625 self.glyph_caches = Some(cache);
626 }
627}
628
629#[cfg(feature = "text")]
630impl ColrRenderer for RenderContext {
631 fn push_clip_layer(&mut self, clip: &BezPath) {
632 Self::push_clip_layer(self, clip);
633 }
634
635 fn push_blend_layer(&mut self, blend_mode: BlendMode) {
636 Self::push_blend_layer(self, blend_mode);
637 }
638
639 fn fill_solid(&mut self, color: AlphaColor<Srgb>) {
640 self.set_paint(color);
641 self.fill_rect(&Rect::new(
642 0.0,
643 0.0,
644 f64::from(self.width),
645 f64::from(self.height),
646 ));
647 }
648
649 fn fill_gradient(&mut self, gradient: crate::peniko::Gradient) {
650 self.set_paint(gradient);
651 self.fill_rect(&Rect::new(
652 0.0,
653 0.0,
654 f64::from(self.width),
655 f64::from(self.height),
656 ));
657 }
658
659 fn set_paint_transform(&mut self, affine: Affine) {
660 Self::set_paint_transform(self, affine);
661 }
662
663 fn pop_layer(&mut self) {
664 Self::pop_layer(self);
665 }
666}
667
668impl Recordable for RenderContext {
669 fn record<F>(&mut self, recording: &mut Recording, f: F)
670 where
671 F: FnOnce(&mut Recorder<'_>),
672 {
673 let mut recorder = Recorder::new(
674 recording,
675 self.transform,
676 #[cfg(feature = "text")]
677 self.take_glyph_caches(),
678 );
679 f(&mut recorder);
680 #[cfg(feature = "text")]
681 {
682 self.glyph_caches = Some(recorder.take_glyph_caches());
683 }
684 }
685
686 fn prepare_recording(&mut self, recording: &mut Recording) {
687 let buffers = recording.take_cached_strips();
688 let (strip_storage, strip_start_indices) =
689 self.generate_strips_from_commands(recording.commands(), buffers);
690 recording.set_cached_strips(strip_storage, strip_start_indices);
691 }
692
693 fn execute_recording(&mut self, recording: &Recording) {
694 let (cached_strips, cached_alphas) = recording.get_cached_strips();
695 let adjusted_strips = self.prepare_cached_strips(cached_strips, cached_alphas);
696
697 let strip_start_indices = recording.get_strip_start_indices();
699 let mut range_index = 0;
700
701 for command in recording.commands() {
703 match command {
704 RenderCommand::FillPath(_)
705 | RenderCommand::StrokePath(_)
706 | RenderCommand::FillRect(_)
707 | RenderCommand::StrokeRect(_) => {
708 self.process_geometry_command(
709 strip_start_indices,
710 range_index,
711 &adjusted_strips,
712 );
713 range_index += 1;
714 }
715 #[cfg(feature = "text")]
716 RenderCommand::FillOutlineGlyph(_) | RenderCommand::StrokeOutlineGlyph(_) => {
717 self.process_geometry_command(
718 strip_start_indices,
719 range_index,
720 &adjusted_strips,
721 );
722 range_index += 1;
723 }
724 RenderCommand::SetPaint(paint) => {
725 self.set_paint(paint.clone());
726 }
727 RenderCommand::SetPaintTransform(transform) => {
728 self.set_paint_transform(*transform);
729 }
730 RenderCommand::ResetPaintTransform => {
731 self.reset_paint_transform();
732 }
733 RenderCommand::SetTransform(transform) => {
734 self.set_transform(*transform);
735 }
736 RenderCommand::SetFillRule(fill_rule) => {
737 self.set_fill_rule(*fill_rule);
738 }
739 RenderCommand::SetStroke(stroke) => {
740 self.set_stroke(stroke.clone());
741 }
742 RenderCommand::PushLayer(PushLayerCommand {
743 clip_path,
744 blend_mode,
745 opacity,
746 mask,
747 }) => {
748 self.push_layer(clip_path.as_ref(), *blend_mode, *opacity, mask.clone());
749 }
750 RenderCommand::PopLayer => {
751 self.pop_layer();
752 }
753 }
754 }
755 }
756}
757
758#[derive(Debug)]
760struct RenderState {
761 transform: Affine,
762 fill_rule: Fill,
763 stroke: Stroke,
764 paint: PaintType,
765 paint_transform: Affine,
766}
767
768impl RenderContext {
770 fn generate_strips_from_commands(
777 &mut self,
778 commands: &[RenderCommand],
779 buffers: (StripStorage, Vec<usize>),
780 ) -> (StripStorage, Vec<usize>) {
781 let (mut strip_storage, mut strip_start_indices) = buffers;
782 strip_storage.clear();
783 strip_storage.set_generation_mode(GenerationMode::Append);
784 strip_start_indices.clear();
785
786 let saved_state = self.take_current_state();
787 let mut strip_generator =
788 StripGenerator::new(self.width, self.height, self.render_settings.level);
789
790 for command in commands {
791 let start_index = strip_storage.strips.len();
792
793 match command {
794 RenderCommand::FillPath(path) => {
795 strip_generator.generate_filled_path(
796 path,
797 self.fill_rule,
798 self.transform,
799 self.aliasing_threshold,
800 &mut strip_storage,
801 );
802 strip_start_indices.push(start_index);
803 }
804 RenderCommand::StrokePath(path) => {
805 strip_generator.generate_stroked_path(
806 path,
807 &self.stroke,
808 self.transform,
809 self.aliasing_threshold,
810 &mut strip_storage,
811 );
812 strip_start_indices.push(start_index);
813 }
814 RenderCommand::FillRect(rect) => {
815 self.rect_to_temp_path(rect);
816 strip_generator.generate_filled_path(
817 &self.temp_path,
818 self.fill_rule,
819 self.transform,
820 self.aliasing_threshold,
821 &mut strip_storage,
822 );
823 strip_start_indices.push(start_index);
824 }
825 RenderCommand::StrokeRect(rect) => {
826 self.rect_to_temp_path(rect);
827 strip_generator.generate_stroked_path(
828 &self.temp_path,
829 &self.stroke,
830 self.transform,
831 self.aliasing_threshold,
832 &mut strip_storage,
833 );
834 strip_start_indices.push(start_index);
835 }
836 #[cfg(feature = "text")]
837 RenderCommand::FillOutlineGlyph((path, glyph_transform)) => {
838 strip_generator.generate_filled_path(
839 path,
840 self.fill_rule,
841 *glyph_transform,
842 self.aliasing_threshold,
843 &mut strip_storage,
844 );
845 strip_start_indices.push(start_index);
846 }
847 #[cfg(feature = "text")]
848 RenderCommand::StrokeOutlineGlyph((path, glyph_transform)) => {
849 strip_generator.generate_stroked_path(
850 path,
851 &self.stroke,
852 *glyph_transform,
853 self.aliasing_threshold,
854 &mut strip_storage,
855 );
856 strip_start_indices.push(start_index);
857 }
858 RenderCommand::SetTransform(transform) => {
859 self.transform = *transform;
860 }
861 RenderCommand::SetFillRule(fill_rule) => {
862 self.fill_rule = *fill_rule;
863 }
864 RenderCommand::SetStroke(stroke) => {
865 self.stroke = stroke.clone();
866 }
867
868 _ => {}
869 }
870 }
871
872 self.restore_state(saved_state);
873
874 (strip_storage, strip_start_indices)
875 }
876}
877
878impl RenderContext {
880 fn process_geometry_command(
881 &mut self,
882 strip_start_indices: &[usize],
883 range_index: usize,
884 adjusted_strips: &[Strip],
885 ) {
886 assert!(
887 range_index < strip_start_indices.len(),
888 "Strip range index out of bounds"
889 );
890 let start = strip_start_indices[range_index];
891 let end = strip_start_indices
892 .get(range_index + 1)
893 .copied()
894 .unwrap_or(adjusted_strips.len());
895 let count = end - start;
896 if count == 0 {
897 return;
899 }
900 assert!(
901 start < adjusted_strips.len() && count > 0,
902 "Invalid strip range"
903 );
904 let paint = self.encode_current_paint();
905 self.dispatcher
906 .generate_wide_cmd(&adjusted_strips[start..end], paint);
907 }
908
909 fn prepare_cached_strips(
911 &mut self,
912 cached_strips: &[Strip],
913 cached_alphas: &[u8],
914 ) -> Vec<Strip> {
915 let alpha_offset = {
917 let storage = self.dispatcher.strip_storage_mut();
918 let offset = storage.alphas.len() as u32;
919 storage.alphas.extend(cached_alphas);
921
922 offset
923 };
924 cached_strips
926 .iter()
927 .map(move |strip| {
928 let mut adjusted_strip = *strip;
929 adjusted_strip.set_alpha_idx(adjusted_strip.alpha_idx() + alpha_offset);
930 adjusted_strip
931 })
932 .collect()
933 }
934
935 fn take_current_state(&mut self) -> RenderState {
937 RenderState {
938 paint: self.paint.clone(),
939 paint_transform: self.paint_transform,
940 transform: self.transform,
941 fill_rule: self.fill_rule,
942 stroke: core::mem::take(&mut self.stroke),
943 }
944 }
945
946 fn restore_state(&mut self, state: RenderState) {
948 self.transform = state.transform;
949 self.fill_rule = state.fill_rule;
950 self.stroke = state.stroke;
951 self.paint = state.paint;
952 self.paint_transform = state.paint_transform;
953 }
954}
955
956#[cfg(test)]
957mod tests {
958 use crate::RenderContext;
959 use vello_common::kurbo::{Rect, Shape};
960 use vello_common::tile::Tile;
961
962 #[test]
963 fn clip_overflow() {
964 let mut ctx = RenderContext::new(100, 100);
965
966 for _ in 0..(usize::from(u16::MAX) + 1).div_ceil(usize::from(Tile::HEIGHT * Tile::WIDTH)) {
967 ctx.fill_rect(&Rect::new(0.0, 0.0, 1.0, 1.0));
968 }
969
970 ctx.push_clip_layer(&Rect::new(20.0, 20.0, 180.0, 180.0).to_path(0.1));
971 ctx.pop_layer();
972 ctx.flush();
973 }
974
975 #[cfg(feature = "multithreading")]
976 #[test]
977 fn multithreaded_crash_after_reset() {
978 use crate::{Level, RenderMode, RenderSettings};
979 use vello_common::pixmap::Pixmap;
980
981 let mut pixmap = Pixmap::new(200, 200);
982 let settings = RenderSettings {
983 level: Level::try_detect().unwrap_or(Level::fallback()),
984 num_threads: 1,
985 render_mode: RenderMode::OptimizeQuality,
986 };
987
988 let mut ctx = RenderContext::new_with(200, 200, settings);
989 ctx.reset();
990 ctx.fill_path(&Rect::new(0.0, 0.0, 100.0, 100.0).to_path(0.1));
991 ctx.flush();
992 ctx.render_to_pixmap(&mut pixmap);
993 ctx.flush();
994 ctx.render_to_pixmap(&mut pixmap);
995 }
996}