1use crate::{
2 internals::{Batch, Draw, GpuBatch, Tessellator, Vertex},
3 FillRule, Images, IntoPaint, LineJoin, Path, Pipeline, Rect, Stroke, Transform,
4};
5
6#[derive(Clone, Copy, Debug)]
7pub struct DrawIndexed {
8 pub start: u32,
9 pub end: u32,
10 pub base_vertex: i32,
11 pub instance: u32,
12}
13
14impl DrawIndexed {
15 #[inline]
16 fn new(start: u32, end: u32, base_vertex: i32, instance: u32) -> Self {
17 Self {
18 start,
19 end,
20 base_vertex,
21 instance,
22 }
23 }
24
25 #[inline]
26 fn call<'a>(
27 &self,
28 rpass: &mut wgpu::RenderBundleEncoder<'a>,
29 pipeline: &'a wgpu::RenderPipeline,
30 ) {
31 let instances = self.instance..self.instance + 1;
32 rpass.set_pipeline(pipeline);
33 rpass.draw_indexed(self.start..self.end, self.base_vertex, instances);
34 }
35}
36
37#[derive(Clone, Copy, Debug)]
38pub enum DrawCall<Key> {
39 Convex(DrawIndexed),
40 ConvexSimple(DrawIndexed),
41 Stencil(DrawIndexed),
42 FringesNonZero(DrawIndexed),
43 FringesEvenOdd(DrawIndexed),
44 QuadNonZero(DrawIndexed),
45 QuadEvenOdd(DrawIndexed),
46 ImagePremultiplied(DrawIndexed),
47 ImageUnmultiplied(DrawIndexed),
48 ImageFont(DrawIndexed),
49
50 BindImage(Key),
51 Stroke {
52 start: u32,
53 end: u32,
54 base_vertex: i32,
55 instance: u32,
56 },
57}
58
59#[cfg_attr(feature = "bevy", derive(bevy::prelude::Component))]
60pub struct Picture(pub(crate) wgpu::RenderBundle);
61
62impl<'a> std::iter::IntoIterator for &'a Picture {
63 type Item = &'a wgpu::RenderBundle;
64 type IntoIter = std::iter::Once<Self::Item>;
65
66 #[inline]
67 fn into_iter(self) -> Self::IntoIter {
68 std::iter::once(&self.0)
69 }
70}
71
72impl Picture {
73 pub fn new<Key: Eq + std::hash::Hash>(
74 device: &wgpu::Device,
75 viewport: &wgpu::BindGroup,
76 offset: u32,
77 pipeline: &Pipeline,
78 batch: &GpuBatch,
79 images: &Images<Key>,
80 calls: &[DrawCall<Key>],
81 ) -> Self {
82 let mut rpass = device.create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {
83 label: Some("reui::Picture"),
84 color_formats: &[Some(wgpu::TextureFormat::Rgba8UnormSrgb)],
85 depth_stencil: Some(wgpu::RenderBundleDepthStencil {
86 format: wgpu::TextureFormat::Depth24PlusStencil8,
87 depth_read_only: true,
88 stencil_read_only: false,
89 }),
90 sample_count: 1,
91 multiview: None,
92 });
93
94 rpass.set_bind_group(0, viewport, &[offset]);
95
96 rpass.set_index_buffer(batch.indices.slice(..), wgpu::IndexFormat::Uint32);
97 rpass.set_vertex_buffer(0, batch.vertices.slice(..));
98 rpass.set_vertex_buffer(1, batch.instances.slice(..));
99
100 for call in calls {
101 match call {
102 DrawCall::BindImage(image) => rpass.set_bind_group(1, &images[image].bind, &[]),
103
104 DrawCall::Convex(draw) => draw.call(&mut rpass, &pipeline.convex),
105 DrawCall::ConvexSimple(draw) => draw.call(&mut rpass, &pipeline.convex_simple),
106 DrawCall::Stencil(draw) => draw.call(&mut rpass, &pipeline.fill_stencil),
107 DrawCall::QuadNonZero(draw) => draw.call(&mut rpass, &pipeline.fill_quad_non_zero),
108 DrawCall::QuadEvenOdd(draw) => draw.call(&mut rpass, &pipeline.fill_quad_even_odd),
109 DrawCall::FringesNonZero(draw) => draw.call(&mut rpass, &pipeline.fringes_non_zero),
110 DrawCall::FringesEvenOdd(draw) => draw.call(&mut rpass, &pipeline.fringes_even_odd),
111 DrawCall::ImagePremultiplied(draw) => {
112 draw.call(&mut rpass, &pipeline.premultiplied)
113 }
114 DrawCall::ImageUnmultiplied(draw) => draw.call(&mut rpass, &pipeline.unmultiplied),
115 DrawCall::ImageFont(draw) => draw.call(&mut rpass, &pipeline.font),
116
117 &DrawCall::Stroke {
118 start,
119 end,
120 base_vertex,
121 instance,
122 } => {
123 rpass.set_pipeline(&pipeline.stroke_base);
124 rpass.draw_indexed(start..end, base_vertex, instance..instance + 1);
125
126 rpass.set_pipeline(&pipeline.fringes_non_zero);
127 rpass.draw_indexed(start..end, base_vertex, instance + 1..instance + 2);
128
129 rpass.set_pipeline(&pipeline.stroke_stencil);
130 rpass.draw_indexed(start..end, base_vertex, 0..1);
131 }
132 }
133 }
134
135 Self(rpass.finish(&wgpu::RenderBundleDescriptor {
136 label: Some("reui::Picture"),
137 }))
138 }
139}
140
141#[derive(Default)]
142#[cfg_attr(feature = "bevy", derive(bevy::prelude::Component))]
143pub struct Recorder<Key> {
144 pub(crate) calls: Vec<DrawCall<Key>>,
145 pub(crate) batch: Batch,
146 pub(crate) cache: Tessellator,
147}
148
149impl<Key> Recorder<Key> {
150 pub fn clear(&mut self) {
151 self.calls.clear();
152 self.batch.clear();
153 self.cache.clear();
154 }
155
156 pub fn stroke(
157 &mut self,
158 path: &Path,
159 paint: impl IntoPaint,
160 mut stroke: Stroke,
161 transform: Transform,
162 antialias: bool,
163 ) {
164 let mut paint = paint.into_paint(transform);
165
166 let average_scale = {
167 let sx = (transform.sx * transform.sx + transform.shx * transform.shx).sqrt();
168 let sy = (transform.shy * transform.shy + transform.sy * transform.sy).sqrt();
169 (sx + sy) * 0.5
170 };
171
172 stroke.width = (stroke.width * average_scale).max(0.0);
173
174 let fringe_width = 1.0;
175
176 if stroke.width < fringe_width {
177 let alpha = (stroke.width / fringe_width).clamp(0.0, 1.0);
180 let coverage = alpha * alpha;
181 paint.inner_color.alpha *= coverage;
182 paint.outer_color.alpha *= coverage;
183 stroke.width = fringe_width;
184 }
185
186 let fringe_width = if antialias { fringe_width } else { 0.0 };
187
188 let commands = path.transform_iter(transform);
189 let tess_tol = 0.25;
190 self.cache.flatten(commands, tess_tol, 0.01);
191
192 stroke.width *= 0.5;
193
194 let base_vertex = self.batch.base_vertex();
195 let indices = self
196 .cache
197 .expand_stroke(&mut self.batch, stroke, fringe_width, tess_tol);
198
199 let stroke_thr = 1.0 - 0.5 / 255.0;
200 let first = paint.to_instance(stroke.width, fringe_width, stroke_thr);
201 let instance = self.batch.instance(first);
202
203 let second = paint.to_instance(stroke.width, fringe_width, -1.0);
204 let _ = self.batch.instance(second);
205
206 self.calls.push(DrawCall::Stroke {
207 start: indices.start,
208 end: indices.end,
209 base_vertex,
210 instance,
211 });
212 }
213
214 pub fn fill(
215 &mut self,
216 path: &Path,
217 paint: impl IntoPaint,
218 transform: Transform,
219 fill_rule: FillRule,
220 antialias: bool,
221 ) {
222 let paint = paint.into_paint(transform);
223
224 let fringe_width = if antialias { 1.0 } else { 0.0 };
225
226 let raw = paint.to_instance(fringe_width, fringe_width, -1.0);
228 let instance = self.batch.instance(raw);
229
230 let commands = path.transform_iter(transform);
231 self.cache.flatten(commands, 0.25, 0.01);
232
233 let draw = self
234 .cache
235 .expand_fill(&mut self.batch, fringe_width, LineJoin::Miter, 2.4);
236
237 match draw {
238 Draw::Convex {
240 base_vertex,
241 start,
242 end,
243 } => {
244 let draw = DrawIndexed::new(start, end, base_vertex, instance);
245 if paint.inner_color == paint.outer_color {
246 self.calls.push(DrawCall::ConvexSimple(draw));
247 } else {
248 self.calls.push(DrawCall::Convex(draw));
249 }
250 }
251 Draw::Concave {
252 base_vertex,
253 fill,
254 stroke,
255 quad,
256 } => {
257 let stenicl = DrawIndexed::new(fill.start, fill.end, base_vertex, instance);
258
259 let stroke = DrawIndexed::new(stroke.start, stroke.end, base_vertex, instance);
260 let quad = DrawIndexed::new(quad.start, quad.end, base_vertex, instance);
261
262 self.calls.push(DrawCall::Stencil(stenicl));
263 self.calls.push(match fill_rule {
264 FillRule::NonZero => DrawCall::FringesNonZero(stroke),
265 FillRule::EvenOdd => DrawCall::FringesEvenOdd(stroke),
266 });
267 self.calls.push(match fill_rule {
268 FillRule::NonZero => DrawCall::QuadNonZero(quad),
269 FillRule::EvenOdd => DrawCall::QuadEvenOdd(quad),
270 });
271 }
272 }
273 }
274
275 pub fn blit_premultiplied(&mut self, rect: Rect, transform: Transform, image: Key) {
276 let Rect { min, max } = rect;
277 let base_vertex = self.batch.base_vertex();
278 let indices = self.batch.push_strip(
279 0,
280 &[
281 Vertex::new([max.x, max.y], [1.0, 1.0]).transform(transform),
282 Vertex::new([max.x, min.y], [1.0, 0.0]).transform(transform),
283 Vertex::new([min.x, max.y], [0.0, 1.0]).transform(transform),
284 Vertex::new([min.x, min.y], [0.0, 0.0]).transform(transform),
285 ],
286 );
287 let draw = DrawIndexed::new(indices.start, indices.end, base_vertex, 0);
288 self.calls.push(DrawCall::BindImage(image));
289 self.calls.push(DrawCall::ImagePremultiplied(draw));
290 }
291
292 pub fn blit_unmultiplied(&mut self, rect: Rect, transform: Transform, image: Key) {
293 let Rect { min, max } = rect;
294 let base_vertex = self.batch.base_vertex();
295 let indices = self.batch.push_strip(
296 0,
297 &[
298 Vertex::new([max.x, max.y], [1.0, 1.0]).transform(transform),
299 Vertex::new([max.x, min.y], [1.0, 0.0]).transform(transform),
300 Vertex::new([min.x, max.y], [0.0, 1.0]).transform(transform),
301 Vertex::new([min.x, min.y], [0.0, 0.0]).transform(transform),
302 ],
303 );
304 let draw = DrawIndexed::new(indices.start, indices.end, base_vertex, 0);
305 self.calls.push(DrawCall::BindImage(image));
306 self.calls.push(DrawCall::ImageUnmultiplied(draw));
307 }
308
309 pub fn blit_font(&mut self, rect: Rect, transform: Transform, image: Key) {
310 let Rect { min, max } = rect;
311 let base_vertex = self.batch.base_vertex();
312 let indices = self.batch.push_strip(
313 0,
314 &[
315 Vertex::new([max.x, max.y], [1.0, 1.0]).transform(transform),
316 Vertex::new([max.x, min.y], [1.0, 0.0]).transform(transform),
317 Vertex::new([min.x, max.y], [0.0, 1.0]).transform(transform),
318 Vertex::new([min.x, min.y], [0.0, 0.0]).transform(transform),
319 ],
320 );
321 let draw = DrawIndexed::new(indices.start, indices.end, base_vertex, 0);
322 self.calls.push(DrawCall::BindImage(image));
323 self.calls.push(DrawCall::ImageFont(draw));
324 }
325}