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::filter_effects::Filter;
22use vello_common::kurbo::{Affine, BezPath, Cap, Join, Rect, Stroke};
23use vello_common::mask::Mask;
24#[cfg(feature = "text")]
25use vello_common::paint::ImageSource;
26use vello_common::paint::{Paint, PaintType};
27use vello_common::peniko::color::palette::css::BLACK;
28use vello_common::peniko::{BlendMode, Fill};
29use vello_common::pixmap::Pixmap;
30use vello_common::recording::{PushLayerCommand, Recordable, Recorder, Recording, RenderCommand};
31use vello_common::strip::Strip;
32use vello_common::strip_generator::{GenerationMode, StripGenerator, StripStorage};
33#[cfg(feature = "text")]
34use vello_common::{
35 color::{AlphaColor, Srgb},
36 colr::{ColrPainter, ColrRenderer},
37 glyph::{GlyphRenderer, GlyphRunBuilder, GlyphType, PreparedGlyph},
38};
39
40#[derive(Debug)]
46pub struct RenderContext {
47 pub(crate) width: u16,
49 pub(crate) height: u16,
51 pub(crate) paint: PaintType,
53 pub(crate) paint_transform: Affine,
55 pub(crate) mask: Option<Mask>,
57 pub(crate) stroke: Stroke,
59 pub(crate) transform: Affine,
61 pub(crate) fill_rule: Fill,
63 pub(crate) blend_mode: BlendMode,
65 pub(crate) temp_path: BezPath,
67 pub(crate) aliasing_threshold: Option<u8>,
69 pub(crate) encoded_paints: Vec<EncodedPaint>,
70 pub(crate) filter: Option<Filter>,
71 #[cfg_attr(
72 not(feature = "text"),
73 allow(dead_code, reason = "used when the `text` feature is enabled")
74 )]
75 pub(crate) render_settings: RenderSettings,
76 dispatcher: Box<dyn Dispatcher>,
77 #[cfg(feature = "text")]
78 pub(crate) glyph_caches: Option<vello_common::glyph::GlyphCaches>,
79}
80
81#[derive(Copy, Clone, Debug)]
83pub struct RenderSettings {
84 pub level: Level,
86 pub num_threads: u16,
89 pub render_mode: RenderMode,
98}
99
100impl Default for RenderSettings {
101 fn default() -> Self {
102 Self {
103 level: Level::try_detect().unwrap_or(Level::fallback()),
104 #[cfg(feature = "multithreading")]
105 num_threads: (std::thread::available_parallelism()
106 .unwrap()
107 .get()
108 .saturating_sub(1) as u16)
109 .min(8),
110 #[cfg(not(feature = "multithreading"))]
111 num_threads: 0,
112 render_mode: RenderMode::OptimizeSpeed,
113 }
114 }
115}
116
117impl RenderContext {
118 pub fn new(width: u16, height: u16) -> Self {
120 Self::new_with(width, height, RenderSettings::default())
121 }
122
123 pub fn new_with(width: u16, height: u16, settings: RenderSettings) -> Self {
125 #[cfg(feature = "multithreading")]
126 let dispatcher: Box<dyn Dispatcher> = if settings.num_threads == 0 {
127 Box::new(SingleThreadedDispatcher::new(width, height, settings.level))
128 } else {
129 Box::new(MultiThreadedDispatcher::new(
130 width,
131 height,
132 settings.num_threads,
133 settings.level,
134 ))
135 };
136
137 #[cfg(not(feature = "multithreading"))]
138 let dispatcher: Box<dyn Dispatcher> =
139 { Box::new(SingleThreadedDispatcher::new(width, height, settings.level)) };
140
141 let transform = Affine::IDENTITY;
142 let fill_rule = Fill::NonZero;
143 let paint = BLACK.into();
144 let paint_transform = Affine::IDENTITY;
145 let stroke = Stroke {
146 width: 1.0,
147 join: Join::Bevel,
148 start_cap: Cap::Butt,
149 end_cap: Cap::Butt,
150 ..Default::default()
151 };
152 let encoded_paints = vec![];
153 let temp_path = BezPath::new();
154 let aliasing_threshold = None;
155
156 Self {
157 width,
158 height,
159 dispatcher,
160 transform,
161 aliasing_threshold,
162 blend_mode: BlendMode::default(),
163 paint,
164 render_settings: settings,
165 mask: None,
166 paint_transform,
167 fill_rule,
168 stroke,
169 temp_path,
170 encoded_paints,
171 filter: None,
172 #[cfg(feature = "text")]
173 glyph_caches: Some(Default::default()),
174 }
175 }
176
177 fn encode_current_paint(&mut self) -> Paint {
178 match self.paint.clone() {
179 PaintType::Solid(s) => s.into(),
180 PaintType::Gradient(g) => {
181 g.encode_into(
183 &mut self.encoded_paints,
184 self.transform * self.paint_transform,
185 )
186 }
187 PaintType::Image(i) => i.encode_into(
188 &mut self.encoded_paints,
189 self.transform * self.paint_transform,
190 ),
191 }
192 }
193
194 pub fn fill_path(&mut self, path: &BezPath) {
196 self.with_optional_filter(|ctx| {
197 let paint = ctx.encode_current_paint();
198 ctx.dispatcher.fill_path(
199 path,
200 ctx.fill_rule,
201 ctx.transform,
202 paint,
203 ctx.blend_mode,
204 ctx.aliasing_threshold,
205 ctx.mask.clone(),
206 &ctx.encoded_paints,
207 );
208 });
209 }
210
211 pub fn stroke_path(&mut self, path: &BezPath) {
213 self.with_optional_filter(|ctx| {
214 let paint = ctx.encode_current_paint();
215 ctx.dispatcher.stroke_path(
216 path,
217 &ctx.stroke,
218 ctx.transform,
219 paint,
220 ctx.blend_mode,
221 ctx.aliasing_threshold,
222 ctx.mask.clone(),
223 &ctx.encoded_paints,
224 );
225 });
226 }
227
228 pub fn fill_rect(&mut self, rect: &Rect) {
230 self.with_optional_filter(|ctx| {
231 ctx.rect_to_temp_path(rect);
232 let paint = ctx.encode_current_paint();
233 ctx.dispatcher.fill_path(
234 &ctx.temp_path,
235 ctx.fill_rule,
236 ctx.transform,
237 paint,
238 ctx.blend_mode,
239 ctx.aliasing_threshold,
240 ctx.mask.clone(),
241 &ctx.encoded_paints,
242 );
243 });
244 }
245
246 pub fn stroke_rect(&mut self, rect: &Rect) {
248 self.with_optional_filter(|ctx| {
249 ctx.rect_to_temp_path(rect);
250 let paint = ctx.encode_current_paint();
251 ctx.dispatcher.stroke_path(
252 &ctx.temp_path,
253 &ctx.stroke,
254 ctx.transform,
255 paint,
256 ctx.blend_mode,
257 ctx.aliasing_threshold,
258 ctx.mask.clone(),
259 &ctx.encoded_paints,
260 );
261 });
262 }
263
264 fn rect_to_temp_path(&mut self, rect: &Rect) {
265 self.temp_path.truncate(0);
266 self.temp_path
267 .push(PathEl::MoveTo(Point::new(rect.x0, rect.y0)));
268 self.temp_path
269 .push(PathEl::LineTo(Point::new(rect.x1, rect.y0)));
270 self.temp_path
271 .push(PathEl::LineTo(Point::new(rect.x1, rect.y1)));
272 self.temp_path
273 .push(PathEl::LineTo(Point::new(rect.x0, rect.y1)));
274 self.temp_path.push(PathEl::ClosePath);
275 }
276
277 pub fn fill_blurred_rounded_rect(&mut self, rect: &Rect, radius: f32, std_dev: f32) {
282 let color = match self.paint {
283 PaintType::Solid(s) => s,
284 _ => BLACK,
286 };
287
288 let blurred_rect = BlurredRoundedRectangle {
289 rect: *rect,
290 color,
291 radius,
292 std_dev,
293 };
294
295 let kernel_size = 2.5 * std_dev;
300 let inflated_rect = rect.inflate(f64::from(kernel_size), f64::from(kernel_size));
301 let transform = self.transform * self.paint_transform;
302
303 self.rect_to_temp_path(&inflated_rect);
304
305 let paint = blurred_rect.encode_into(&mut self.encoded_paints, transform);
306 self.dispatcher.fill_path(
307 &self.temp_path,
308 Fill::NonZero,
309 self.transform,
310 paint,
311 self.blend_mode,
312 self.aliasing_threshold,
313 self.mask.clone(),
314 &self.encoded_paints,
315 );
316 }
317
318 #[cfg(feature = "text")]
320 pub fn glyph_run(&mut self, font: &crate::peniko::FontData) -> GlyphRunBuilder<'_, Self> {
321 GlyphRunBuilder::new(font.clone(), self.transform, self)
322 }
323
324 pub fn push_layer(
330 &mut self,
331 clip_path: Option<&BezPath>,
332 blend_mode: Option<BlendMode>,
333 opacity: Option<f32>,
334 mask: Option<Mask>,
335 filter: Option<Filter>,
336 ) {
337 let mask = mask.and_then(|m| {
338 if m.width() != self.width || m.height() != self.height {
339 None
340 } else {
341 Some(m)
342 }
343 });
344
345 let blend_mode = blend_mode.unwrap_or_default();
346 let opacity = opacity.unwrap_or(1.0);
347
348 self.dispatcher.push_layer(
349 clip_path,
350 self.fill_rule,
351 self.transform,
352 blend_mode,
353 opacity,
354 self.aliasing_threshold,
355 mask,
356 filter,
357 );
358 }
359
360 pub fn push_clip_layer(&mut self, path: &BezPath) {
365 self.push_layer(Some(path), None, None, None, None);
366 }
367
368 pub fn push_blend_layer(&mut self, blend_mode: BlendMode) {
370 self.push_layer(None, Some(blend_mode), None, None, None);
371 }
372
373 pub fn push_opacity_layer(&mut self, opacity: f32) {
375 self.push_layer(None, None, Some(opacity), None, None);
376 }
377
378 pub fn push_mask_layer(&mut self, mask: Mask) {
385 self.push_layer(None, None, None, Some(mask), None);
386 }
387
388 pub fn push_filter_layer(&mut self, filter: Filter) {
394 self.push_layer(None, None, None, None, Some(filter));
395 }
396
397 pub fn set_aliasing_threshold(&mut self, aliasing_threshold: Option<u8>) {
409 self.aliasing_threshold = aliasing_threshold;
410 }
411
412 pub fn pop_layer(&mut self) {
414 self.dispatcher.pop_layer();
415 }
416
417 pub fn set_stroke(&mut self, stroke: Stroke) {
419 self.stroke = stroke;
420 }
421
422 pub fn stroke(&self) -> &Stroke {
424 &self.stroke
425 }
426
427 pub fn set_paint(&mut self, paint: impl Into<PaintType>) {
429 self.paint = paint.into();
430 }
431
432 pub fn paint(&self) -> &PaintType {
434 &self.paint
435 }
436
437 pub fn set_blend_mode(&mut self, blend_mode: BlendMode) {
439 self.blend_mode = blend_mode;
440 }
441
442 pub fn blend_mode(&self) -> BlendMode {
444 self.blend_mode
445 }
446
447 pub fn set_paint_transform(&mut self, paint_transform: Affine) {
453 self.paint_transform = paint_transform;
454 }
455
456 pub fn paint_transform(&self) -> &Affine {
458 &self.paint_transform
459 }
460
461 pub fn reset_paint_transform(&mut self) {
463 self.paint_transform = Affine::IDENTITY;
464 }
465
466 pub fn set_fill_rule(&mut self, fill_rule: Fill) {
468 self.fill_rule = fill_rule;
469 }
470
471 pub fn set_mask(&mut self, mask: Mask) {
478 self.mask = Some(mask);
479 }
480
481 pub fn reset_mask(&mut self) {
483 self.mask = None;
484 }
485
486 pub fn fill_rule(&self) -> &Fill {
488 &self.fill_rule
489 }
490
491 pub fn set_transform(&mut self, transform: Affine) {
493 self.transform = transform;
494 }
495
496 pub fn transform(&self) -> &Affine {
498 &self.transform
499 }
500
501 pub fn reset_transform(&mut self) {
503 self.transform = Affine::IDENTITY;
504 }
505
506 pub fn set_filter_effect(&mut self, filter: Filter) {
511 self.filter = Some(filter);
512 }
513
514 pub fn reset_filter_effect(&mut self) {
516 self.filter = None;
517 }
518
519 pub fn reset(&mut self) {
521 self.dispatcher.reset();
522 self.encoded_paints.clear();
523 self.mask = None;
524 self.reset_transform();
525 self.reset_paint_transform();
526 #[cfg(feature = "text")]
527 self.glyph_caches.as_mut().unwrap().maintain();
528 self.blend_mode = BlendMode::default();
529 }
530
531 pub fn push_clip_path(&mut self, path: &BezPath) {
536 self.dispatcher.push_clip_path(
537 path,
538 self.fill_rule,
539 self.transform,
540 self.aliasing_threshold,
541 );
542 }
543
544 pub fn pop_clip_path(&mut self) {
549 self.dispatcher.pop_clip_path();
550 }
551
552 pub fn flush(&mut self) {
558 self.dispatcher.flush(&self.encoded_paints);
559 }
560
561 pub fn render_to_buffer(
564 &self,
565 buffer: &mut [u8],
566 width: u16,
567 height: u16,
568 render_mode: RenderMode,
569 ) {
570 let wide = self.dispatcher.wide();
572 assert!(!wide.has_layers(), "some layers haven't been popped yet");
573 assert_eq!(
574 buffer.len(),
575 (width as usize) * (height as usize) * 4,
576 "provided width ({}) and height ({}) do not match buffer size ({})",
577 width,
578 height,
579 buffer.len(),
580 );
581
582 self.dispatcher
583 .rasterize(buffer, render_mode, width, height, &self.encoded_paints);
584 }
585
586 pub fn render_to_pixmap(&self, pixmap: &mut Pixmap) {
588 let width = pixmap.width();
589 let height = pixmap.height();
590 self.render_to_buffer(
591 pixmap.data_as_u8_slice_mut(),
592 width,
593 height,
594 self.render_settings.render_mode,
595 );
596 }
597
598 pub fn width(&self) -> u16 {
600 self.width
601 }
602
603 pub fn height(&self) -> u16 {
605 self.height
606 }
607
608 pub fn render_settings(&self) -> &RenderSettings {
610 &self.render_settings
611 }
612
613 fn with_optional_filter<F>(&mut self, mut f: F)
615 where
616 F: FnMut(&mut Self),
617 {
618 if let Some(filter) = self.filter.clone() {
619 self.push_filter_layer(filter);
620 f(self);
621 self.pop_layer();
622 } else {
623 f(self);
624 }
625 }
626}
627
628#[cfg(feature = "text")]
629impl GlyphRenderer for RenderContext {
630 fn fill_glyph(&mut self, prepared_glyph: PreparedGlyph<'_>) {
631 match prepared_glyph.glyph_type {
632 GlyphType::Outline(glyph) => {
633 let paint = self.encode_current_paint();
634 self.dispatcher.fill_path(
635 glyph.path,
636 Fill::NonZero,
637 prepared_glyph.transform,
638 paint,
639 self.blend_mode,
640 self.aliasing_threshold,
641 self.mask.clone(),
642 &self.encoded_paints,
643 );
644 }
645 GlyphType::Bitmap(glyph) => {
646 use vello_common::peniko::ImageSampler;
651 let old_transform = self.transform;
652 let old_paint = self.paint.clone();
653
654 let quality = if prepared_glyph.transform.as_coeffs()[0] < 0.5
656 || prepared_glyph.transform.as_coeffs()[3] < 0.5
657 {
658 crate::peniko::ImageQuality::High
659 } else {
660 crate::peniko::ImageQuality::Medium
661 };
662
663 let image = vello_common::paint::Image {
664 image: ImageSource::Pixmap(Arc::new(glyph.pixmap)),
665 sampler: ImageSampler {
666 x_extend: crate::peniko::Extend::Pad,
667 y_extend: crate::peniko::Extend::Pad,
668 quality,
669 alpha: 1.0,
670 },
671 };
672
673 self.set_paint(image);
674 self.set_transform(prepared_glyph.transform);
675 self.fill_rect(&glyph.area);
676
677 self.set_paint(old_paint);
679 self.transform = old_transform;
680 }
681 GlyphType::Colr(glyph) => {
682 use vello_common::peniko::ImageSampler;
685 let old_transform = self.transform;
686 let old_paint = self.paint.clone();
687 let context_color = match old_paint {
688 PaintType::Solid(s) => s,
689 _ => BLACK,
690 };
691
692 let area = glyph.area;
693
694 let glyph_pixmap = {
695 let settings = RenderSettings {
696 level: self.render_settings.level,
697 render_mode: self.render_settings.render_mode,
698 num_threads: 0,
699 };
700
701 let mut ctx = Self::new_with(glyph.pix_width, glyph.pix_height, settings);
702 let mut pix = Pixmap::new(glyph.pix_width, glyph.pix_height);
703
704 let mut colr_painter = ColrPainter::new(glyph, context_color, &mut ctx);
705 colr_painter.paint();
706
707 ctx.flush();
710 ctx.render_to_pixmap(&mut pix);
711
712 pix
713 };
714
715 let image = vello_common::paint::Image {
716 image: ImageSource::Pixmap(Arc::new(glyph_pixmap)),
717 sampler: ImageSampler {
718 x_extend: crate::peniko::Extend::Pad,
719 y_extend: crate::peniko::Extend::Pad,
720 quality: crate::peniko::ImageQuality::Low,
723 alpha: 1.0,
724 },
725 };
726
727 self.set_paint(image);
728 self.set_transform(prepared_glyph.transform);
729 self.fill_rect(&area);
730
731 self.set_paint(old_paint);
733 self.transform = old_transform;
734 }
735 }
736 }
737
738 fn stroke_glyph(&mut self, prepared_glyph: PreparedGlyph<'_>) {
739 match prepared_glyph.glyph_type {
740 GlyphType::Outline(glyph) => {
741 let paint = self.encode_current_paint();
742 self.dispatcher.stroke_path(
743 glyph.path,
744 &self.stroke,
745 prepared_glyph.transform,
746 paint,
747 self.blend_mode,
748 self.aliasing_threshold,
749 self.mask.clone(),
750 &self.encoded_paints,
751 );
752 }
753 GlyphType::Bitmap(_) | GlyphType::Colr(_) => {
754 self.fill_glyph(prepared_glyph);
757 }
758 }
759 }
760
761 fn take_glyph_caches(&mut self) -> vello_common::glyph::GlyphCaches {
762 self.glyph_caches.take().unwrap()
763 }
764
765 fn restore_glyph_caches(&mut self, cache: vello_common::glyph::GlyphCaches) {
766 self.glyph_caches = Some(cache);
767 }
768}
769
770#[cfg(feature = "text")]
771impl ColrRenderer for RenderContext {
772 fn push_clip_layer(&mut self, clip: &BezPath) {
773 Self::push_clip_layer(self, clip);
774 }
775
776 fn push_blend_layer(&mut self, blend_mode: BlendMode) {
777 Self::push_blend_layer(self, blend_mode);
778 }
779
780 fn fill_solid(&mut self, color: AlphaColor<Srgb>) {
781 self.set_paint(color);
782 self.fill_rect(&Rect::new(
783 0.0,
784 0.0,
785 f64::from(self.width),
786 f64::from(self.height),
787 ));
788 }
789
790 fn fill_gradient(&mut self, gradient: crate::peniko::Gradient) {
791 self.set_paint(gradient);
792 self.fill_rect(&Rect::new(
793 0.0,
794 0.0,
795 f64::from(self.width),
796 f64::from(self.height),
797 ));
798 }
799
800 fn set_paint_transform(&mut self, affine: Affine) {
801 Self::set_paint_transform(self, affine);
802 }
803
804 fn pop_layer(&mut self) {
805 Self::pop_layer(self);
806 }
807}
808
809impl Recordable for RenderContext {
810 fn record<F>(&mut self, recording: &mut Recording, f: F)
811 where
812 F: FnOnce(&mut Recorder<'_>),
813 {
814 let mut recorder = Recorder::new(
815 recording,
816 self.transform,
817 #[cfg(feature = "text")]
818 self.take_glyph_caches(),
819 );
820 f(&mut recorder);
821 #[cfg(feature = "text")]
822 {
823 self.glyph_caches = Some(recorder.take_glyph_caches());
824 }
825 }
826
827 fn prepare_recording(&mut self, recording: &mut Recording) {
828 let buffers = recording.take_cached_strips();
829 let (strip_storage, strip_start_indices) =
830 self.generate_strips_from_commands(recording.commands(), buffers);
831 recording.set_cached_strips(strip_storage, strip_start_indices);
832 }
833
834 fn execute_recording(&mut self, recording: &Recording) {
835 let (cached_strips, cached_alphas) = recording.get_cached_strips();
836 let adjusted_strips = self.prepare_cached_strips(cached_strips, cached_alphas);
837
838 let strip_start_indices = recording.get_strip_start_indices();
840 let mut range_index = 0;
841
842 for command in recording.commands() {
844 match command {
845 RenderCommand::FillPath(_)
846 | RenderCommand::StrokePath(_)
847 | RenderCommand::FillRect(_)
848 | RenderCommand::StrokeRect(_) => {
849 self.process_geometry_command(
850 strip_start_indices,
851 range_index,
852 &adjusted_strips,
853 );
854 range_index += 1;
855 }
856 #[cfg(feature = "text")]
857 RenderCommand::FillOutlineGlyph(_) | RenderCommand::StrokeOutlineGlyph(_) => {
858 self.process_geometry_command(
859 strip_start_indices,
860 range_index,
861 &adjusted_strips,
862 );
863 range_index += 1;
864 }
865 RenderCommand::SetPaint(paint) => {
866 self.set_paint(paint.clone());
867 }
868 RenderCommand::SetPaintTransform(transform) => {
869 self.set_paint_transform(*transform);
870 }
871 RenderCommand::ResetPaintTransform => {
872 self.reset_paint_transform();
873 }
874 RenderCommand::SetTransform(transform) => {
875 self.set_transform(*transform);
876 }
877 RenderCommand::SetFillRule(fill_rule) => {
878 self.set_fill_rule(*fill_rule);
879 }
880 RenderCommand::SetStroke(stroke) => {
881 self.set_stroke(stroke.clone());
882 }
883 RenderCommand::SetFilterEffect(filter) => {
884 self.set_filter_effect(filter.clone());
885 }
886 RenderCommand::ResetFilterEffect => {
887 self.reset_filter_effect();
888 }
889 RenderCommand::PushLayer(PushLayerCommand {
890 clip_path,
891 blend_mode,
892 opacity,
893 mask,
894 filter,
895 }) => {
896 self.push_layer(
897 clip_path.as_ref(),
898 *blend_mode,
899 *opacity,
900 mask.clone(),
901 filter.clone(),
902 );
903 }
904 RenderCommand::PopLayer => {
905 self.pop_layer();
906 }
907 }
908 }
909 }
910}
911
912#[derive(Debug)]
914struct RenderState {
915 transform: Affine,
916 fill_rule: Fill,
917 stroke: Stroke,
918 paint: PaintType,
919 paint_transform: Affine,
920}
921
922impl RenderContext {
924 fn generate_strips_from_commands(
931 &mut self,
932 commands: &[RenderCommand],
933 buffers: (StripStorage, Vec<usize>),
934 ) -> (StripStorage, Vec<usize>) {
935 let (mut strip_storage, mut strip_start_indices) = buffers;
936 strip_storage.clear();
937 strip_storage.set_generation_mode(GenerationMode::Append);
938 strip_start_indices.clear();
939
940 let saved_state = self.take_current_state();
941 let mut strip_generator =
942 StripGenerator::new(self.width, self.height, self.render_settings.level);
943
944 for command in commands {
945 let start_index = strip_storage.strips.len();
946
947 match command {
948 RenderCommand::FillPath(path) => {
949 strip_generator.generate_filled_path(
950 path,
951 self.fill_rule,
952 self.transform,
953 self.aliasing_threshold,
954 &mut strip_storage,
955 None,
956 );
957 strip_start_indices.push(start_index);
958 }
959 RenderCommand::StrokePath(path) => {
960 strip_generator.generate_stroked_path(
961 path,
962 &self.stroke,
963 self.transform,
964 self.aliasing_threshold,
965 &mut strip_storage,
966 None,
967 );
968 strip_start_indices.push(start_index);
969 }
970 RenderCommand::FillRect(rect) => {
971 self.rect_to_temp_path(rect);
972 strip_generator.generate_filled_path(
973 &self.temp_path,
974 self.fill_rule,
975 self.transform,
976 self.aliasing_threshold,
977 &mut strip_storage,
978 None,
979 );
980 strip_start_indices.push(start_index);
981 }
982 RenderCommand::StrokeRect(rect) => {
983 self.rect_to_temp_path(rect);
984 strip_generator.generate_stroked_path(
985 &self.temp_path,
986 &self.stroke,
987 self.transform,
988 self.aliasing_threshold,
989 &mut strip_storage,
990 None,
991 );
992 strip_start_indices.push(start_index);
993 }
994 #[cfg(feature = "text")]
995 RenderCommand::FillOutlineGlyph((path, glyph_transform)) => {
996 strip_generator.generate_filled_path(
997 path,
998 self.fill_rule,
999 *glyph_transform,
1000 self.aliasing_threshold,
1001 &mut strip_storage,
1002 None,
1003 );
1004 strip_start_indices.push(start_index);
1005 }
1006 #[cfg(feature = "text")]
1007 RenderCommand::StrokeOutlineGlyph((path, glyph_transform)) => {
1008 strip_generator.generate_stroked_path(
1009 path,
1010 &self.stroke,
1011 *glyph_transform,
1012 self.aliasing_threshold,
1013 &mut strip_storage,
1014 None,
1015 );
1016 strip_start_indices.push(start_index);
1017 }
1018 RenderCommand::SetTransform(transform) => {
1019 self.transform = *transform;
1020 }
1021 RenderCommand::SetFillRule(fill_rule) => {
1022 self.fill_rule = *fill_rule;
1023 }
1024 RenderCommand::SetStroke(stroke) => {
1025 self.stroke = stroke.clone();
1026 }
1027
1028 _ => {}
1029 }
1030 }
1031
1032 self.restore_state(saved_state);
1033
1034 (strip_storage, strip_start_indices)
1035 }
1036}
1037
1038impl RenderContext {
1040 fn process_geometry_command(
1041 &mut self,
1042 strip_start_indices: &[usize],
1043 range_index: usize,
1044 adjusted_strips: &[Strip],
1045 ) {
1046 assert!(
1047 range_index < strip_start_indices.len(),
1048 "Strip range index out of bounds"
1049 );
1050 let start = strip_start_indices[range_index];
1051 let end = strip_start_indices
1052 .get(range_index + 1)
1053 .copied()
1054 .unwrap_or(adjusted_strips.len());
1055 let count = end - start;
1056 if count == 0 {
1057 return;
1059 }
1060 assert!(
1061 start < adjusted_strips.len() && count > 0,
1062 "Invalid strip range"
1063 );
1064 let paint = self.encode_current_paint();
1065 self.dispatcher.generate_wide_cmd(
1066 &adjusted_strips[start..end],
1067 paint,
1068 self.blend_mode,
1069 &self.encoded_paints,
1070 );
1071 }
1072
1073 fn prepare_cached_strips(
1075 &mut self,
1076 cached_strips: &[Strip],
1077 cached_alphas: &[u8],
1078 ) -> Vec<Strip> {
1079 let alpha_offset = {
1081 let storage = self.dispatcher.strip_storage_mut();
1082 let offset = storage.alphas.len() as u32;
1083 storage.alphas.extend(cached_alphas);
1085
1086 offset
1087 };
1088 cached_strips
1090 .iter()
1091 .map(move |strip| {
1092 let mut adjusted_strip = *strip;
1093 adjusted_strip.set_alpha_idx(adjusted_strip.alpha_idx() + alpha_offset);
1094 adjusted_strip
1095 })
1096 .collect()
1097 }
1098
1099 fn take_current_state(&mut self) -> RenderState {
1101 RenderState {
1102 paint: self.paint.clone(),
1103 paint_transform: self.paint_transform,
1104 transform: self.transform,
1105 fill_rule: self.fill_rule,
1106 stroke: core::mem::take(&mut self.stroke),
1107 }
1108 }
1109
1110 fn restore_state(&mut self, state: RenderState) {
1112 self.transform = state.transform;
1113 self.fill_rule = state.fill_rule;
1114 self.stroke = state.stroke;
1115 self.paint = state.paint;
1116 self.paint_transform = state.paint_transform;
1117 }
1118}
1119
1120#[cfg(test)]
1121mod tests {
1122 use crate::RenderContext;
1123 use vello_common::kurbo::{Rect, Shape};
1124 use vello_common::tile::Tile;
1125
1126 #[test]
1127 fn clip_overflow() {
1128 let mut ctx = RenderContext::new(100, 100);
1129
1130 for _ in 0..(usize::from(u16::MAX) + 1).div_ceil(usize::from(Tile::HEIGHT * Tile::WIDTH)) {
1131 ctx.fill_rect(&Rect::new(0.0, 0.0, 1.0, 1.0));
1132 }
1133
1134 ctx.push_clip_layer(&Rect::new(20.0, 20.0, 180.0, 180.0).to_path(0.1));
1135 ctx.pop_layer();
1136 ctx.flush();
1137 }
1138
1139 #[cfg(feature = "multithreading")]
1140 #[test]
1141 fn multithreaded_crash_after_reset() {
1142 use crate::{Level, RenderMode, RenderSettings};
1143 use vello_common::pixmap::Pixmap;
1144
1145 let mut pixmap = Pixmap::new(200, 200);
1146 let settings = RenderSettings {
1147 level: Level::try_detect().unwrap_or(Level::fallback()),
1148 num_threads: 1,
1149 render_mode: RenderMode::OptimizeQuality,
1150 };
1151
1152 let mut ctx = RenderContext::new_with(200, 200, settings);
1153 ctx.reset();
1154 ctx.fill_path(&Rect::new(0.0, 0.0, 100.0, 100.0).to_path(0.1));
1155 ctx.flush();
1156 ctx.render_to_pixmap(&mut pixmap);
1157 ctx.flush();
1158 ctx.render_to_pixmap(&mut pixmap);
1159 }
1160}