1use std::collections::HashMap;
6
7use kurbo::{Affine, BezPath, Rect as KRect, Shape};
8use peniko::{BlendMode, Compose, ImageBrush, Mix};
9use vello_cpu::{Pixmap, RenderContext, Resources};
10
11use crate::display::List;
12use crate::render::{Draw, Paint};
13#[cfg(test)]
14use crate::resolve::{Scene, Theme};
15use crate::tiles::{TILE, tile_rect};
16
17pub struct Cpu<'a> {
19 pub ctx: &'a mut RenderContext,
20 pub sources: HashMap<u64, vello_cpu::ImageSource>,
21 pub resources: Resources,
22}
23
24impl crate::display::Target for Cpu<'_> {
25 fn draw(&mut self, draw: &Draw, t: Affine) {
26 let ctx = &mut *self.ctx;
27 ctx.set_transform(t);
28 match draw {
29 Draw::Fill(path, paint) => {
30 match paint {
31 Paint::Solid(c) => ctx.set_paint(*c),
32 Paint::Gradient(g) => ctx.set_paint(g.clone()),
33 Paint::Image(data, place) => {
34 let image = self
35 .sources
36 .entry(data.data.id())
37 .or_insert_with(|| vello_cpu::ImageSource::from_peniko_image_data(data))
38 .clone();
39 ctx.set_paint(ImageBrush {
40 image,
41 sampler: Default::default(),
42 });
43 ctx.set_paint_transform(*place);
44 }
45 }
46 ctx.fill_path(path);
47 ctx.reset_paint_transform();
48 }
49 Draw::Stroke(path, width, c) => {
50 ctx.set_paint(*c);
51 ctx.set_stroke(kurbo::Stroke::new(*width as f64));
52 ctx.stroke_path(path);
53 }
54 Draw::Glyphs(font, size, c, coords, glyphs) => {
55 ctx.set_paint(*c);
56 let _ = ctx
57 .glyph_run(&mut self.resources, font)
58 .font_size(*size)
59 .normalized_coords(coords)
60 .hint(true)
61 .fill_glyphs(glyphs.iter().map(|g| vello_cpu::Glyph {
62 id: g.id,
63 x: g.x,
64 y: g.y,
65 }));
66 }
67 }
68 }
69 fn push_clip(&mut self, path: &BezPath, t: Affine) {
70 self.ctx.set_transform(t);
71 self.ctx.push_clip_layer(path);
72 }
73 fn push_layer(
74 &mut self,
75 blend: crate::display::Blend,
76 bounds: Option<&BezPath>,
77 t: Affine,
78 area: KRect,
79 ) {
80 let whole;
81 let bounds = match bounds {
82 Some(b) => {
83 self.ctx.set_transform(t);
84 b
85 }
86 None => {
87 self.ctx.set_transform(Affine::IDENTITY);
88 whole = area.to_path(0.1);
89 &whole
90 }
91 };
92 match blend {
93 crate::display::Blend::Opacity(o) => {
94 self.ctx.push_layer(Some(bounds), None, Some(o), None, None)
95 }
96 crate::display::Blend::Add => self.ctx.push_layer(
97 Some(bounds),
98 Some(BlendMode::new(Mix::Normal, Compose::Plus)),
99 None,
100 None,
101 None,
102 ),
103 }
104 }
105 fn pop(&mut self) {
106 self.ctx.pop_layer();
107 }
108}
109
110pub fn paint_area(list: &crate::display::List, area: KRect, scale: f64) -> Pixmap {
113 let side = |v: f64| ((v * scale - 1e-6).ceil().clamp(1.0, u16::MAX as f64)) as u16;
116 let (w, h) = (side(area.width()), side(area.height()));
117 let mut ctx = RenderContext::new(w, h);
118 let mut cpu = Cpu {
119 ctx: &mut ctx,
120 sources: HashMap::new(),
121 resources: Resources::default(),
122 };
123 crate::display::paint(list, area, scale, &mut cpu);
124 let mut resources = std::mem::take(&mut cpu.resources);
125 ctx.flush();
126 let mut pixmap = Pixmap::new(w, h);
127 ctx.render(&mut pixmap, &mut resources);
128 pixmap
129}
130
131#[cfg(test)]
132impl crate::render::Renderer {
133 pub fn paint(&mut self, scene: &Scene, theme: Theme, scale: f32) -> Pixmap {
135 let d = self.drawing(scene, theme, None).expect("drawable");
136 let a = d.area();
137 let area = KRect::new(
138 a.x as f64,
139 a.y as f64,
140 (a.x + a.width) as f64,
141 (a.y + a.height) as f64,
142 );
143 paint_area(&d.list, area, scale as f64)
144 }
145}
146
147pub fn tile(list: &List, scale: f64, col: u32, row: u32) -> Vec<u8> {
149 let pixmap = paint_area(list, tile_rect(list, scale, col, row), scale);
150 let mut rgba = pixmap.data_as_u8_slice().to_vec();
151 unpremultiply(&mut rgba);
152 rgba
153}
154
155pub fn strip(list: &List, col: u32, y: u32, rows: u32) -> Vec<u8> {
159 let (x0, y0) = (list.area.x as f64, list.area.y as f64);
160 let area = KRect::new(
161 x0 + (col * TILE) as f64,
162 y0 + y as f64,
163 x0 + ((col + 1) * TILE) as f64,
164 y0 + (y + rows) as f64,
165 );
166 let mut rgba = paint_area(list, area, 1.0).data_as_u8_slice().to_vec();
167 unpremultiply(&mut rgba);
168 rgba
169}
170
171pub fn unpremultiply(rgba: &mut [u8]) {
173 for px in rgba.as_chunks_mut::<4>().0 {
174 let a = px[3] as u32;
175 if a != 0 && a != 255 {
176 for c in &mut px[..3] {
177 *c = ((*c as u32 * 255 + a / 2) / a).min(255) as u8;
178 }
179 }
180 }
181}