1use anyhow::Result;
2use bytemuck::{Pod, Zeroable};
3
4use crate::allocator::{BufKey, OwnedBuffer, RenderAllocator};
5use crate::display_list::{Command, DisplayList, ExternalTextureId};
6use crate::scene::{
7 Brush, FillRule, Path, PathCmd, Rect, RoundedRect, Stroke, TextRun, Transform2D,
8};
9
10#[repr(C)]
11#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
12pub struct Vertex {
13 pub pos: [f32; 2],
14 pub color: [f32; 4],
15 pub z_index: f32,
16}
17
18pub struct GpuScene {
19 pub vertex: OwnedBuffer,
20 pub index: OwnedBuffer,
21 pub vertices: u32,
22 pub indices: u32,
23}
24
25#[derive(Clone, Debug)]
27pub struct ExtractedTextDraw {
28 pub run: TextRun,
29 pub z: i32,
30 pub transform: Transform2D,
31}
32
33#[derive(Clone, Debug)]
35pub struct ExtractedImageDraw {
36 pub path: std::path::PathBuf,
37 pub origin: [f32; 2],
38 pub size: [f32; 2],
39 pub z: i32,
40 pub transform: Transform2D,
41 pub opacity: f32,
42}
43
44#[derive(Clone, Debug)]
46pub struct ExtractedSvgDraw {
47 pub path: std::path::PathBuf,
48 pub origin: [f32; 2],
49 pub size: [f32; 2],
50 pub z: i32,
51 pub transform: Transform2D,
52 pub opacity: f32,
53}
54
55#[derive(Clone, Debug)]
57pub struct ExtractedExternalTextureDraw {
58 pub texture_id: ExternalTextureId,
59 pub origin: [f32; 2],
60 pub size: [f32; 2],
61 pub z: i32,
62 pub opacity: f32,
63 pub premultiplied: bool,
64}
65
66#[derive(Clone, Copy, Debug)]
68pub struct TransparentBatch {
69 pub z: i32,
70 pub index_start: u32,
71 pub index_count: u32,
72}
73
74pub struct UnifiedSceneData {
76 pub gpu_scene: GpuScene,
77 pub transparent_gpu_scene: GpuScene,
78 pub transparent_batches: Vec<TransparentBatch>,
79 pub text_draws: Vec<ExtractedTextDraw>,
80 pub image_draws: Vec<ExtractedImageDraw>,
81 pub svg_draws: Vec<ExtractedSvgDraw>,
82 pub external_texture_draws: Vec<ExtractedExternalTextureDraw>,
83}
84
85fn apply_transform(p: [f32; 2], t: Transform2D) -> [f32; 2] {
86 let [a, b, c, d, e, f] = t.m;
87 [a * p[0] + c * p[1] + e, b * p[0] + d * p[1] + f]
88}
89
90fn rect_to_verts(rect: Rect, color: [f32; 4], t: Transform2D, z: f32) -> ([Vertex; 4], [u16; 6]) {
91 let x0 = rect.x;
92 let y0 = rect.y;
93 let x1 = rect.x + rect.w;
94 let y1 = rect.y + rect.h;
95 let p0 = apply_transform([x0, y0], t);
96 let p1 = apply_transform([x1, y0], t);
97 let p2 = apply_transform([x1, y1], t);
98 let p3 = apply_transform([x0, y1], t);
99 (
100 [
101 Vertex {
102 pos: p0,
103 color,
104 z_index: z,
105 },
106 Vertex {
107 pos: p1,
108 color,
109 z_index: z,
110 },
111 Vertex {
112 pos: p2,
113 color,
114 z_index: z,
115 },
116 Vertex {
117 pos: p3,
118 color,
119 z_index: z,
120 },
121 ],
122 [0, 1, 2, 0, 2, 3],
123 )
124}
125
126fn push_rect_linear_gradient(
127 vertices: &mut Vec<Vertex>,
128 indices: &mut Vec<u16>,
129 rect: Rect,
130 stops: &[(f32, [f32; 4])],
131 t: Transform2D,
132 z: f32,
133) {
134 if stops.len() < 2 {
135 return;
136 }
137 let mut s = stops.to_vec();
139 s.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
140 let y0 = rect.y;
141 let y1 = rect.y + rect.h;
142 for pair in s.windows(2) {
143 let (t0, c0) = (pair[0].0.clamp(0.0, 1.0), pair[0].1);
144 let (t1, c1) = (pair[1].0.clamp(0.0, 1.0), pair[1].1);
145 if (t1 - t0).abs() < 1e-6 {
146 continue;
147 }
148 let x0 = rect.x + rect.w * t0;
149 let x1 = rect.x + rect.w * t1;
150 let p0 = apply_transform([x0, y0], t);
151 let p1 = apply_transform([x1, y0], t);
152 let p2 = apply_transform([x1, y1], t);
153 let p3 = apply_transform([x0, y1], t);
154 let base = vertices.len() as u16;
155 vertices.extend_from_slice(&[
156 Vertex {
157 pos: p0,
158 color: c0,
159 z_index: z,
160 },
161 Vertex {
162 pos: p1,
163 color: c1,
164 z_index: z,
165 },
166 Vertex {
167 pos: p2,
168 color: c1,
169 z_index: z,
170 },
171 Vertex {
172 pos: p3,
173 color: c0,
174 z_index: z,
175 },
176 ]);
177 indices.extend_from_slice(&[base, base + 1, base + 2, base, base + 2, base + 3]);
178 }
179}
180
181fn normalize_gradient_stops(stops: &[(f32, [f32; 4])]) -> Vec<(f32, [f32; 4])> {
182 if stops.is_empty() {
183 return Vec::new();
184 }
185 let mut out = stops.to_vec();
186 out.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
187 if out.first().map(|s| s.0).unwrap_or(0.0) > 0.0
188 && let Some((_, c)) = out.first().copied()
189 {
190 out.insert(0, (0.0, c));
191 }
192 if out.last().map(|s| s.0).unwrap_or(1.0) < 1.0
193 && let Some((_, c)) = out.last().copied()
194 {
195 out.push((1.0, c));
196 }
197 for stop in &mut out {
198 stop.0 = stop.0.clamp(0.0, 1.0);
199 }
200 out
201}
202
203fn linear_to_srgb(c: f32) -> f32 {
204 let x = c.clamp(0.0, 1.0);
205 if x <= 0.0031308 {
206 12.92 * x
207 } else {
208 1.055 * x.powf(1.0 / 2.4) - 0.055
209 }
210}
211
212fn srgb_to_linear(c: f32) -> f32 {
213 let x = c.clamp(0.0, 1.0);
214 if x <= 0.04045 {
215 x / 12.92
216 } else {
217 ((x + 0.055) / 1.055).powf(2.4)
218 }
219}
220
221pub fn lerp_color(a: [f32; 4], b: [f32; 4], t: f32) -> [f32; 4] {
222 let ar = linear_to_srgb(a[0]);
224 let ag = linear_to_srgb(a[1]);
225 let ab = linear_to_srgb(a[2]);
226 let br = linear_to_srgb(b[0]);
227 let bg = linear_to_srgb(b[1]);
228 let bb = linear_to_srgb(b[2]);
229
230 let rr = ar + (br - ar) * t;
231 let rg = ag + (bg - ag) * t;
232 let rb = ab + (bb - ab) * t;
233
234 [
235 srgb_to_linear(rr),
236 srgb_to_linear(rg),
237 srgb_to_linear(rb),
238 a[3] + (b[3] - a[3]) * t,
239 ]
240}
241
242pub fn sample_gradient_stops(stops: &[(f32, [f32; 4])], t: f32) -> [f32; 4] {
243 if stops.is_empty() {
244 return [0.0, 0.0, 0.0, 0.0];
245 }
246 let tc = t.clamp(0.0, 1.0);
247 if tc <= stops[0].0 {
248 return stops[0].1;
249 }
250 if tc >= stops[stops.len() - 1].0 {
251 return stops[stops.len() - 1].1;
252 }
253
254 for pair in stops.windows(2) {
255 let (t0, c0) = pair[0];
256 let (t1, c1) = pair[1];
257 if tc >= t0 && tc <= t1 {
258 let span = (t1 - t0).max(1e-6);
259 let local_t = ((tc - t0) / span).clamp(0.0, 1.0);
260 return lerp_color(c0, c1, local_t);
261 }
262 }
263 stops[stops.len() - 1].1
264}
265
266fn push_ellipse(
267 vertices: &mut Vec<Vertex>,
268 indices: &mut Vec<u16>,
269 center: [f32; 2],
270 radii: [f32; 2],
271 color: [f32; 4],
272 z: f32,
273 t: Transform2D,
274) {
275 let segs = 64u32;
276 let base = vertices.len() as u16;
277 let c = apply_transform(center, t);
278 vertices.push(Vertex {
279 pos: c,
280 color,
281 z_index: z,
282 });
283
284 for i in 0..segs {
285 let theta = (i as f32) / (segs as f32) * std::f32::consts::TAU;
286 let p = [
287 center[0] + radii[0] * theta.cos(),
288 center[1] + radii[1] * theta.sin(),
289 ];
290 let p = apply_transform(p, t);
291 vertices.push(Vertex {
292 pos: p,
293 color,
294 z_index: z,
295 });
296 }
297 for i in 0..segs {
298 let i0 = base;
299 let i1 = base + 1 + i as u16;
300 let i2 = base + 1 + ((i + 1) % segs) as u16;
301 indices.extend_from_slice(&[i0, i1, i2]);
302 }
303}
304
305fn push_ellipse_radial_gradient(
306 vertices: &mut Vec<Vertex>,
307 indices: &mut Vec<u16>,
308 center: [f32; 2],
309 radii: [f32; 2],
310 stops: &[(f32, [f32; 4])],
311 z: f32,
312 t: Transform2D,
313) {
314 if stops.len() < 2 {
315 return;
316 }
317 let mut s = stops.to_vec();
318 s.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
319 let segs = 64u32;
320 let base_center = vertices.len() as u16;
321 let cpos = apply_transform(center, t);
323 vertices.push(Vertex {
324 pos: cpos,
325 color: s[0].1,
326 z_index: z,
327 });
328
329 let mut prev_ring_start = vertices.len() as u16;
331 let prev_color = s[0].1;
332 let prev_t0 = s[0].0.clamp(0.0, 1.0);
333 let prev_t = if prev_t0 <= 0.0 { 0.0 } else { prev_t0 };
334 for i in 0..segs {
335 let theta = (i as f32) / (segs as f32) * std::f32::consts::TAU;
336 let p = [
337 center[0] + radii[0] * prev_t * theta.cos(),
338 center[1] + radii[1] * prev_t * theta.sin(),
339 ];
340 let p = apply_transform(p, t);
341 vertices.push(Vertex {
342 pos: p,
343 color: prev_color,
344 z_index: z,
345 });
346 }
347 if prev_t == 0.0 {
349 for i in 0..segs {
350 let i1 = base_center;
351 let i2 = prev_ring_start + i as u16;
352 let i3 = prev_ring_start + ((i + 1) % segs) as u16;
353 indices.extend_from_slice(&[i1, i2, i3]);
354 }
355 }
356
357 for si in 1..s.len() {
358 let (tcur, ccur) = (s[si].0.clamp(0.0, 1.0), s[si].1);
359 let ring_start = vertices.len() as u16;
360 for i in 0..segs {
361 let theta = (i as f32) / (segs as f32) * std::f32::consts::TAU;
362 let p = [
363 center[0] + radii[0] * tcur * theta.cos(),
364 center[1] + radii[1] * tcur * theta.sin(),
365 ];
366 let p = apply_transform(p, t);
367 vertices.push(Vertex {
368 pos: p,
369 color: ccur,
370 z_index: z,
371 });
372 }
373 for i in 0..segs {
375 let a0 = prev_ring_start + i as u16;
376 let a1 = prev_ring_start + ((i + 1) % segs) as u16;
377 let b0 = ring_start + i as u16;
378 let b1 = ring_start + ((i + 1) % segs) as u16;
379 indices.extend_from_slice(&[a0, b0, b1, a0, b1, a1]);
380 }
381 prev_ring_start = ring_start;
382 }
383}
384
385fn push_rounded_rect_linear_gradient(
386 vertices: &mut Vec<Vertex>,
387 indices: &mut Vec<u16>,
388 rrect: RoundedRect,
389 start: [f32; 2],
390 end: [f32; 2],
391 stops: &[(f32, [f32; 4])],
392 z: f32,
393 t: Transform2D,
394) {
395 let packed = normalize_gradient_stops(stops);
396 if packed.len() < 2 {
397 return;
398 }
399
400 let path = rounded_rect_to_path(rrect);
401 let dx = end[0] - start[0];
402 let dy = end[1] - start[1];
403 let denom = (dx * dx + dy * dy).max(1e-6);
404
405 tessellate_path_fill_with_color_fn(vertices, indices, &path, z, t, |p| {
406 let proj = ((p[0] - start[0]) * dx + (p[1] - start[1]) * dy) / denom;
407 sample_gradient_stops(&packed, proj)
408 });
409}
410
411fn push_rounded_rect_radial_gradient(
412 vertices: &mut Vec<Vertex>,
413 indices: &mut Vec<u16>,
414 rrect: RoundedRect,
415 center: [f32; 2],
416 radius: f32,
417 stops: &[(f32, [f32; 4])],
418 z: f32,
419 t: Transform2D,
420) {
421 let packed = normalize_gradient_stops(stops);
422 if packed.len() < 2 {
423 return;
424 }
425
426 let path = rounded_rect_to_path(rrect);
427 let r = radius.abs().max(1e-6);
428 tessellate_path_fill_subdivided_with_color_fn(vertices, indices, &path, z, t, 6.0, |p| {
429 let dx = p[0] - center[0];
430 let dy = p[1] - center[1];
431 let dist = (dx * dx + dy * dy).sqrt();
432 sample_gradient_stops(&packed, dist / r)
433 });
434}
435
436fn tessellate_path_fill(
437 vertices: &mut Vec<Vertex>,
438 indices: &mut Vec<u16>,
439 path: &Path,
440 color: [f32; 4],
441 z: f32,
442 t: Transform2D,
443) {
444 tessellate_path_fill_with_color_fn(vertices, indices, path, z, t, |_| color);
445}
446
447fn tessellate_path_fill_with_color_fn<F>(
448 vertices: &mut Vec<Vertex>,
449 indices: &mut Vec<u16>,
450 path: &Path,
451 z: f32,
452 t: Transform2D,
453 mut color_at: F,
454) where
455 F: FnMut([f32; 2]) -> [f32; 4],
456{
457 let Some(geom) = tessellate_path_fill_geometry(path) else {
458 return;
459 };
460
461 if vertices.len() > u16::MAX as usize {
463 return;
464 }
465 let base = vertices.len() as u16;
466 for p in &geom.vertices {
467 let tp = apply_transform(*p, t);
468 vertices.push(Vertex {
469 pos: tp,
470 color: color_at(*p),
471 z_index: z,
472 });
473 }
474 indices.extend(geom.indices.iter().map(|i| base + *i));
475}
476
477fn tessellate_path_fill_subdivided_with_color_fn<F>(
478 vertices: &mut Vec<Vertex>,
479 indices: &mut Vec<u16>,
480 path: &Path,
481 z: f32,
482 t: Transform2D,
483 max_edge: f32,
484 mut color_at: F,
485) where
486 F: FnMut([f32; 2]) -> [f32; 4],
487{
488 let Some(geom) = tessellate_path_fill_geometry(path) else {
489 return;
490 };
491
492 let max_edge = max_edge.max(1.0);
493 for tri in geom.indices.chunks_exact(3) {
494 let p0 = geom.vertices[tri[0] as usize];
495 let p1 = geom.vertices[tri[1] as usize];
496 let p2 = geom.vertices[tri[2] as usize];
497 append_subdivided_triangle(vertices, indices, p0, p1, p2, z, t, max_edge, &mut color_at);
498 }
499}
500
501fn tessellate_path_fill_geometry(
502 path: &Path,
503) -> Option<lyon_tessellation::VertexBuffers<[f32; 2], u16>> {
504 use lyon_geom::point;
505 use lyon_path::Path as LyonPath;
506 use lyon_tessellation::{
507 BuffersBuilder, FillOptions, FillTessellator, FillVertex, VertexBuffers,
508 };
509
510 let mut builder = lyon_path::Path::builder();
511 let mut started = false;
512 for cmd in &path.cmds {
513 match *cmd {
514 PathCmd::MoveTo(p) => {
515 if started {
516 builder.end(false);
517 }
518 builder.begin(point(p[0], p[1]));
519 started = true;
520 }
521 PathCmd::LineTo(p) => {
522 if !started {
523 builder.begin(point(p[0], p[1]));
524 started = true;
525 } else {
526 builder.line_to(point(p[0], p[1]));
527 }
528 }
529 PathCmd::QuadTo(c, p) => {
530 builder.quadratic_bezier_to(point(c[0], c[1]), point(p[0], p[1]));
531 }
532 PathCmd::CubicTo(c1, c2, p) => {
533 builder.cubic_bezier_to(
534 point(c1[0], c1[1]),
535 point(c2[0], c2[1]),
536 point(p[0], p[1]),
537 );
538 }
539 PathCmd::Close => {
540 builder.end(true);
541 started = false;
542 }
543 }
544 }
545 if started {
546 builder.end(false);
547 }
548
549 let lyon_path: LyonPath = builder.build();
550 let mut tess = FillTessellator::new();
551 let tol = std::env::var("LYON_TOLERANCE")
552 .ok()
553 .and_then(|v| v.parse::<f32>().ok())
554 .unwrap_or(0.1);
555 let base_opts = FillOptions::default().with_tolerance(tol);
556 let options = match path.fill_rule {
557 FillRule::NonZero => base_opts.with_fill_rule(lyon_tessellation::FillRule::NonZero),
558 FillRule::EvenOdd => base_opts.with_fill_rule(lyon_tessellation::FillRule::EvenOdd),
559 };
560
561 let mut geom: VertexBuffers<[f32; 2], u16> = VertexBuffers::new();
562 let result = tess.tessellate_path(
563 lyon_path.as_slice(),
564 &options,
565 &mut BuffersBuilder::new(&mut geom, |fv: FillVertex| {
566 let p = fv.position();
567 [p.x, p.y]
568 }),
569 );
570 if result.is_err() {
571 return None;
572 }
573 Some(geom)
574}
575
576fn append_subdivided_triangle<F>(
577 vertices: &mut Vec<Vertex>,
578 indices: &mut Vec<u16>,
579 p0: [f32; 2],
580 p1: [f32; 2],
581 p2: [f32; 2],
582 z: f32,
583 t: Transform2D,
584 max_edge: f32,
585 color_at: &mut F,
586) where
587 F: FnMut([f32; 2]) -> [f32; 4],
588{
589 let edge_len = |a: [f32; 2], b: [f32; 2]| -> f32 {
590 let dx = b[0] - a[0];
591 let dy = b[1] - a[1];
592 (dx * dx + dy * dy).sqrt()
593 };
594 let max_len = edge_len(p0, p1).max(edge_len(p1, p2)).max(edge_len(p2, p0));
595 let steps = ((max_len / max_edge).ceil() as usize).clamp(1, 12);
596 let steps_f = steps as f32;
597
598 let base = vertices.len() as u16;
599 let mut row_offsets = Vec::with_capacity(steps + 1);
600 let mut offset = 0usize;
601 for row in 0..=steps {
602 row_offsets.push(offset);
603 offset += steps - row + 1;
604 }
605
606 for i in 0..=steps {
607 for j in 0..=(steps - i) {
608 let a = i as f32 / steps_f;
609 let b = j as f32 / steps_f;
610 let c = 1.0 - a - b;
611 let p = [
612 p0[0] * c + p1[0] * a + p2[0] * b,
613 p0[1] * c + p1[1] * a + p2[1] * b,
614 ];
615 vertices.push(Vertex {
616 pos: apply_transform(p, t),
617 color: color_at(p),
618 z_index: z,
619 });
620 }
621 }
622
623 let tri_area = (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p1[1] - p0[1]) * (p2[0] - p0[0]);
624 let ccw = tri_area >= 0.0;
625 let idx = |row: usize, col: usize| -> u16 { base + (row_offsets[row] + col) as u16 };
626
627 for i in 0..steps {
628 for j in 0..(steps - i) {
629 let a = idx(i, j);
630 let b = idx(i + 1, j);
631 let c = idx(i, j + 1);
632
633 if ccw {
634 indices.extend_from_slice(&[a, b, c]);
635 } else {
636 indices.extend_from_slice(&[a, c, b]);
637 }
638
639 if j < (steps - i - 1) {
640 let d = idx(i + 1, j + 1);
641 if ccw {
642 indices.extend_from_slice(&[b, d, c]);
643 } else {
644 indices.extend_from_slice(&[b, c, d]);
645 }
646 }
647 }
648 }
649}
650
651fn tessellate_path_stroke(
652 vertices: &mut Vec<Vertex>,
653 indices: &mut Vec<u16>,
654 path: &Path,
655 stroke: Stroke,
656 color: [f32; 4],
657 z: f32,
658 t: Transform2D,
659) {
660 use lyon_geom::point;
661 use lyon_path::Path as LyonPath;
662 use lyon_tessellation::{
663 BuffersBuilder, LineCap, LineJoin, StrokeOptions, StrokeTessellator, StrokeVertex,
664 VertexBuffers,
665 };
666
667 let mut builder = lyon_path::Path::builder();
669 let mut started = false;
670 for cmd in &path.cmds {
671 match *cmd {
672 PathCmd::MoveTo(p) => {
673 if started {
674 builder.end(false);
675 }
676 builder.begin(point(p[0], p[1]));
677 started = true;
678 }
679 PathCmd::LineTo(p) => {
680 if !started {
681 builder.begin(point(p[0], p[1]));
682 started = true;
683 } else {
684 builder.line_to(point(p[0], p[1]));
685 }
686 }
687 PathCmd::QuadTo(c, p) => {
688 builder.quadratic_bezier_to(point(c[0], c[1]), point(p[0], p[1]));
689 }
690 PathCmd::CubicTo(c1, c2, p) => {
691 builder.cubic_bezier_to(
692 point(c1[0], c1[1]),
693 point(c2[0], c2[1]),
694 point(p[0], p[1]),
695 );
696 }
697 PathCmd::Close => {
698 builder.end(true);
699 started = false;
700 }
701 }
702 }
703 if started {
705 builder.end(false);
706 }
707 let lyon_path: LyonPath = builder.build();
708
709 let mut tess = StrokeTessellator::new();
710 let tol = std::env::var("LYON_TOLERANCE")
711 .ok()
712 .and_then(|v| v.parse::<f32>().ok())
713 .unwrap_or(0.1);
714 let options = StrokeOptions::default()
715 .with_line_width(stroke.width.max(0.0))
716 .with_tolerance(tol)
717 .with_line_join(LineJoin::Round)
718 .with_start_cap(LineCap::Round)
719 .with_end_cap(LineCap::Round);
720 let mut geom: VertexBuffers<[f32; 2], u16> = VertexBuffers::new();
721 let result = tess.tessellate_path(
722 lyon_path.as_slice(),
723 &options,
724 &mut BuffersBuilder::new(&mut geom, |sv: StrokeVertex| {
725 let p = sv.position();
726 [p.x, p.y]
727 }),
728 );
729 if result.is_err() {
730 return;
731 }
732 let base = vertices.len() as u16;
733 for p in &geom.vertices {
734 let tp = apply_transform(*p, t);
735 vertices.push(Vertex {
736 pos: tp,
737 color,
738 z_index: z,
739 });
740 }
741 indices.extend(geom.indices.iter().map(|i| base + *i));
742}
743
744fn rounded_rect_to_path(rrect: RoundedRect) -> Path {
747 let rect = rrect.rect;
748 let mut tl = rrect.radii.tl.min(rect.w * 0.5).min(rect.h * 0.5);
749 let mut tr = rrect.radii.tr.min(rect.w * 0.5).min(rect.h * 0.5);
750 let mut br = rrect.radii.br.min(rect.w * 0.5).min(rect.h * 0.5);
751 let mut bl = rrect.radii.bl.min(rect.w * 0.5).min(rect.h * 0.5);
752
753 for r in [&mut tl, &mut tr, &mut br, &mut bl] {
755 if !r.is_finite() || *r < 0.0 {
756 *r = 0.0;
757 }
758 }
759
760 if tl <= 0.0 && tr <= 0.0 && br <= 0.0 && bl <= 0.0 {
762 return Path {
763 cmds: vec![
764 PathCmd::MoveTo([rect.x, rect.y]),
765 PathCmd::LineTo([rect.x + rect.w, rect.y]),
766 PathCmd::LineTo([rect.x + rect.w, rect.y + rect.h]),
767 PathCmd::LineTo([rect.x, rect.y + rect.h]),
768 PathCmd::Close,
769 ],
770 fill_rule: FillRule::NonZero,
771 };
772 }
773
774 const K: f32 = 0.552_284_749_831;
776 let x0 = rect.x;
777 let y0 = rect.y;
778 let x1 = rect.x + rect.w;
779 let y1 = rect.y + rect.h;
780
781 let mut cmds: Vec<PathCmd> = Vec::new();
783 cmds.push(PathCmd::MoveTo([x0 + tl, y0]));
784
785 cmds.push(PathCmd::LineTo([x1 - tr, y0]));
787 if tr > 0.0 {
789 let c1 = [x1 - tr + K * tr, y0];
790 let c2 = [x1, y0 + tr - K * tr];
791 let p = [x1, y0 + tr];
792 cmds.push(PathCmd::CubicTo(c1, c2, p));
793 } else {
794 cmds.push(PathCmd::LineTo([x1, y0]));
795 cmds.push(PathCmd::LineTo([x1, y0 + tr]));
796 }
797
798 cmds.push(PathCmd::LineTo([x1, y1 - br]));
800 if br > 0.0 {
802 let c1 = [x1, y1 - br + K * br];
803 let c2 = [x1 - br + K * br, y1];
804 let p = [x1 - br, y1];
805 cmds.push(PathCmd::CubicTo(c1, c2, p));
806 } else {
807 cmds.push(PathCmd::LineTo([x1, y1]));
808 cmds.push(PathCmd::LineTo([x1 - br, y1]));
809 }
810
811 cmds.push(PathCmd::LineTo([x0 + bl, y1]));
813 if bl > 0.0 {
815 let c1 = [x0 + bl - K * bl, y1];
816 let c2 = [x0, y1 - bl + K * bl];
817 let p = [x0, y1 - bl];
818 cmds.push(PathCmd::CubicTo(c1, c2, p));
819 } else {
820 cmds.push(PathCmd::LineTo([x0, y1]));
821 cmds.push(PathCmd::LineTo([x0, y1 - bl]));
822 }
823
824 cmds.push(PathCmd::LineTo([x0, y0 + tl]));
826 if tl > 0.0 {
828 let c1 = [x0, y0 + tl - K * tl];
829 let c2 = [x0 + tl - K * tl, y0];
830 let p = [x0 + tl, y0];
831 cmds.push(PathCmd::CubicTo(c1, c2, p));
832 } else {
833 cmds.push(PathCmd::LineTo([x0, y0]));
834 cmds.push(PathCmd::LineTo([x0 + tl, y0]));
835 }
836
837 cmds.push(PathCmd::Close);
838 Path {
839 cmds,
840 fill_rule: FillRule::NonZero,
841 }
842}
843
844fn push_rounded_rect(
845 vertices: &mut Vec<Vertex>,
846 indices: &mut Vec<u16>,
847 rrect: RoundedRect,
848 color: [f32; 4],
849 z: f32,
850 t: Transform2D,
851) {
852 let path = rounded_rect_to_path(rrect);
854 tessellate_path_fill(vertices, indices, &path, color, z, t);
855}
856
857fn push_rect_stroke(
858 vertices: &mut Vec<Vertex>,
859 indices: &mut Vec<u16>,
860 rect: Rect,
861 stroke: Stroke,
862 color: [f32; 4],
863 z: f32,
864 t: Transform2D,
865) {
866 let w = stroke.width.max(0.0);
867 if w <= 0.0001 {
868 return;
869 }
870
871 let use_aa = w <= 1.5;
875 let (outer_color, inner_color) = if use_aa {
876 let mut oc = [0.0; 4];
877 let scale = 0.0f32;
880 oc[0] = color[0] * scale;
881 oc[1] = color[1] * scale;
882 oc[2] = color[2] * scale;
883 oc[3] = color[3] * scale;
884 (oc, color)
885 } else {
886 (color, color)
887 };
888
889 let o0 = apply_transform([rect.x, rect.y], t);
891 let o1 = apply_transform([rect.x + rect.w, rect.y], t);
892 let o2 = apply_transform([rect.x + rect.w, rect.y + rect.h], t);
893 let o3 = apply_transform([rect.x, rect.y + rect.h], t);
894 let ix0 = rect.x + w;
896 let iy0 = rect.y + w;
897 let ix1 = (rect.x + rect.w - w).max(ix0);
898 let iy1 = (rect.y + rect.h - w).max(iy0);
899 let i0 = apply_transform([ix0, iy0], t);
900 let i1 = apply_transform([ix1, iy0], t);
901 let i2 = apply_transform([ix1, iy1], t);
902 let i3 = apply_transform([ix0, iy1], t);
903
904 let base = vertices.len() as u16;
905 vertices.extend_from_slice(&[
906 Vertex {
907 pos: o0,
908 color: outer_color,
909 z_index: z,
910 }, Vertex {
912 pos: o1,
913 color: outer_color,
914 z_index: z,
915 }, Vertex {
917 pos: o2,
918 color: outer_color,
919 z_index: z,
920 }, Vertex {
922 pos: o3,
923 color: outer_color,
924 z_index: z,
925 }, Vertex {
927 pos: i0,
928 color: inner_color,
929 z_index: z,
930 }, Vertex {
932 pos: i1,
933 color: inner_color,
934 z_index: z,
935 }, Vertex {
937 pos: i2,
938 color: inner_color,
939 z_index: z,
940 }, Vertex {
942 pos: i3,
943 color: inner_color,
944 z_index: z,
945 }, ]);
947 let idx: [u16; 24] = [
949 0, 1, 5, 0, 5, 4, 1, 2, 6, 1, 6, 5, 2, 3, 7, 2, 7, 6, 3, 0, 4, 3, 4, 7,
954 ];
955 indices.extend(idx.iter().map(|i| base + i));
956}
957
958fn push_rounded_rect_stroke(
959 vertices: &mut Vec<Vertex>,
960 indices: &mut Vec<u16>,
961 rrect: RoundedRect,
962 stroke: Stroke,
963 color: [f32; 4],
964 z: f32,
965 t: Transform2D,
966) {
967 let w = stroke.width.max(0.0);
968 if w <= 0.0001 {
969 return;
970 }
971 let path = rounded_rect_to_path(rrect);
972 tessellate_path_stroke(vertices, indices, &path, Stroke { width: w }, color, z, t);
973}
974
975pub fn upload_display_list(
976 allocator: &mut RenderAllocator,
977 queue: &wgpu::Queue,
978 list: &DisplayList,
979) -> Result<GpuScene> {
980 let mut vertices: Vec<Vertex> = Vec::new();
981 let mut indices: Vec<u16> = Vec::new();
982
983 for cmd in &list.commands {
991 match cmd {
992 Command::DrawRect {
993 rect,
994 brush,
995 transform,
996 z,
997 ..
998 } => {
999 match brush {
1000 Brush::Solid(col) => {
1001 let color = [col.r, col.g, col.b, col.a];
1002 let (v, i) = rect_to_verts(*rect, color, *transform, *z as f32);
1003 let base = vertices.len() as u16;
1004 vertices.extend_from_slice(&v);
1005 indices.extend(i.iter().map(|idx| base + idx));
1006 }
1007 Brush::LinearGradient { stops, .. } => {
1008 let mut packed: Vec<(f32, [f32; 4])> = stops
1010 .iter()
1011 .map(|(tpos, c)| (*tpos, [c.r, c.g, c.b, c.a]))
1012 .collect();
1013 if packed.is_empty() {
1014 continue;
1015 }
1016 if packed.first().unwrap().0 > 0.0 {
1018 let c = packed.first().unwrap().1;
1019 packed.insert(0, (0.0, c));
1020 }
1021 if packed.last().unwrap().0 < 1.0 {
1022 let c = packed.last().unwrap().1;
1023 packed.push((1.0, c));
1024 }
1025 push_rect_linear_gradient(
1026 &mut vertices,
1027 &mut indices,
1028 *rect,
1029 &packed,
1030 *transform,
1031 *z as f32,
1032 );
1033 }
1034 _ => {}
1035 }
1036 }
1037 Command::DrawRoundedRect {
1038 rrect,
1039 brush,
1040 transform,
1041 z,
1042 ..
1043 } => match brush {
1044 Brush::Solid(col) => {
1045 let color = [col.r, col.g, col.b, col.a];
1046 push_rounded_rect(
1047 &mut vertices,
1048 &mut indices,
1049 *rrect,
1050 color,
1051 *z as f32,
1052 *transform,
1053 );
1054 }
1055 Brush::LinearGradient { start, end, stops } => {
1056 let packed: Vec<(f32, [f32; 4])> = stops
1057 .iter()
1058 .map(|(tpos, c)| (*tpos, [c.r, c.g, c.b, c.a]))
1059 .collect();
1060 if packed.is_empty() {
1061 continue;
1062 }
1063 push_rounded_rect_linear_gradient(
1064 &mut vertices,
1065 &mut indices,
1066 *rrect,
1067 *start,
1068 *end,
1069 &packed,
1070 *z as f32,
1071 *transform,
1072 );
1073 }
1074 Brush::RadialGradient {
1075 center,
1076 radius,
1077 stops,
1078 } => {
1079 let packed: Vec<(f32, [f32; 4])> = stops
1080 .iter()
1081 .map(|(tpos, c)| (*tpos, [c.r, c.g, c.b, c.a]))
1082 .collect();
1083 if packed.is_empty() {
1084 continue;
1085 }
1086 push_rounded_rect_radial_gradient(
1087 &mut vertices,
1088 &mut indices,
1089 *rrect,
1090 *center,
1091 *radius,
1092 &packed,
1093 *z as f32,
1094 *transform,
1095 );
1096 }
1097 },
1098 Command::StrokeRect {
1099 rect,
1100 stroke,
1101 brush,
1102 transform,
1103 z,
1104 ..
1105 } => {
1106 if let Brush::Solid(col) = brush {
1107 let color = [col.r, col.g, col.b, col.a];
1108 push_rect_stroke(
1109 &mut vertices,
1110 &mut indices,
1111 *rect,
1112 *stroke,
1113 color,
1114 *z as f32,
1115 *transform,
1116 );
1117 }
1118 }
1119 Command::StrokeRoundedRect {
1120 rrect,
1121 stroke,
1122 brush,
1123 transform,
1124 z,
1125 ..
1126 } => {
1127 if let Brush::Solid(col) = brush {
1128 let color = [col.r, col.g, col.b, col.a];
1129 push_rounded_rect_stroke(
1130 &mut vertices,
1131 &mut indices,
1132 *rrect,
1133 *stroke,
1134 color,
1135 *z as f32,
1136 *transform,
1137 );
1138 }
1139 }
1140 Command::DrawEllipse {
1141 center,
1142 radii,
1143 brush,
1144 transform,
1145 z,
1146 ..
1147 } => match brush {
1148 Brush::Solid(col) => {
1149 let color = [col.r, col.g, col.b, col.a];
1150 push_ellipse(
1151 &mut vertices,
1152 &mut indices,
1153 *center,
1154 *radii,
1155 color,
1156 *z as f32,
1157 *transform,
1158 );
1159 }
1160 Brush::RadialGradient {
1161 center: _gcenter,
1162 radius: _r,
1163 stops,
1164 } => {
1165 let mut packed: Vec<(f32, [f32; 4])> = stops
1166 .iter()
1167 .map(|(t, c)| (*t, [c.r, c.g, c.b, c.a]))
1168 .collect();
1169 if packed.is_empty() {
1170 continue;
1171 }
1172 if packed.first().unwrap().0 > 0.0 {
1173 let c = packed.first().unwrap().1;
1174 packed.insert(0, (0.0, c));
1175 }
1176 if packed.last().unwrap().0 < 1.0 {
1177 let c = packed.last().unwrap().1;
1178 packed.push((1.0, c));
1179 }
1180 push_ellipse_radial_gradient(
1181 &mut vertices,
1182 &mut indices,
1183 *center,
1184 *radii,
1185 &packed,
1186 *z as f32,
1187 *transform,
1188 );
1189 }
1190 _ => {}
1191 },
1192 Command::FillPath {
1193 path,
1194 color,
1195 transform,
1196 z,
1197 ..
1198 } => {
1199 let col = [color.r, color.g, color.b, color.a];
1200 tessellate_path_fill(
1201 &mut vertices,
1202 &mut indices,
1203 path,
1204 col,
1205 *z as f32,
1206 *transform,
1207 );
1208 }
1209 Command::StrokePath {
1210 path,
1211 stroke,
1212 color,
1213 transform,
1214 z,
1215 ..
1216 } => {
1217 let col = [color.r, color.g, color.b, color.a];
1218 tessellate_path_stroke(
1219 &mut vertices,
1220 &mut indices,
1221 path,
1222 *stroke,
1223 col,
1224 *z as f32,
1225 *transform,
1226 );
1227 }
1228 Command::BoxShadow { .. } => {}
1230 Command::HitRegionRect { .. } => {}
1232 Command::HitRegionRoundedRect { .. } => {}
1233 Command::HitRegionEllipse { .. } => {}
1234 _ => {}
1235 }
1236 }
1237
1238 if (indices.len() % 2) != 0 {
1240 if indices.len() >= 3 {
1241 let a = indices[indices.len() - 3];
1242 let b = indices[indices.len() - 2];
1243 let c = indices[indices.len() - 1];
1244 indices.extend_from_slice(&[a, b, c]);
1245 } else {
1246 indices.push(0);
1247 }
1248 }
1249
1250 let vsize = (vertices.len() * std::mem::size_of::<Vertex>()) as u64;
1252 let isize = (indices.len() * std::mem::size_of::<u16>()) as u64;
1253 let vbuf = allocator.allocate_buffer(BufKey {
1254 size: vsize.max(4),
1255 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1256 });
1257 let ibuf = allocator.allocate_buffer(BufKey {
1258 size: isize.max(4),
1259 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1260 });
1261 if vsize > 0 {
1262 queue.write_buffer(&vbuf.buffer, 0, bytemuck::cast_slice(&vertices));
1263 }
1264 if isize > 0 {
1265 queue.write_buffer(&ibuf.buffer, 0, bytemuck::cast_slice(&indices));
1266 }
1267
1268 Ok(GpuScene {
1269 vertex: vbuf,
1270 index: ibuf,
1271 vertices: vertices.len() as u32,
1272 indices: indices.len() as u32,
1273 })
1274}
1275
1276pub fn upload_display_list_unified(
1285 allocator: &mut RenderAllocator,
1286 queue: &wgpu::Queue,
1287 list: &DisplayList,
1288) -> Result<UnifiedSceneData> {
1289 let mut vertices: Vec<Vertex> = Vec::new();
1290 let mut indices: Vec<u16> = Vec::new();
1291 let mut transparent_vertices: Vec<Vertex> = Vec::new();
1292 let mut transparent_indices: Vec<u16> = Vec::new();
1293 let mut transparent_batches: Vec<TransparentBatch> = Vec::new();
1294 let mut text_draws: Vec<ExtractedTextDraw> = Vec::new();
1295 let mut image_draws: Vec<ExtractedImageDraw> = Vec::new();
1296 let mut svg_draws: Vec<ExtractedSvgDraw> = Vec::new();
1297 let mut external_texture_draws: Vec<ExtractedExternalTextureDraw> = Vec::new();
1298 let is_transparent = |alpha: f32| alpha < 0.999;
1299 let record_transparent_batch =
1300 |batches: &mut Vec<TransparentBatch>, z: i32, index_start: usize, index_end: usize| {
1301 if index_end <= index_start {
1302 return;
1303 }
1304 let start = index_start as u32;
1305 let count = (index_end - index_start) as u32;
1306 if let Some(last) = batches.last_mut()
1307 && last.z == z
1308 && last.index_start + last.index_count == start
1309 {
1310 last.index_count += count;
1311 } else {
1312 batches.push(TransparentBatch {
1313 z,
1314 index_start: start,
1315 index_count: count,
1316 });
1317 }
1318 };
1319
1320 let mut transform_stack: Vec<Transform2D> = vec![Transform2D::identity()];
1325 let mut _current_transform = Transform2D::identity();
1326
1327 let mut opacity_stack: Vec<f32> = vec![1.0];
1332 let current_opacity = |stack: &[f32]| *stack.last().unwrap_or(&1.0);
1333
1334 fn premul_opa(c: [f32; 4], o: f32) -> [f32; 4] {
1337 if o >= 0.999 {
1338 return c;
1339 }
1340 [c[0] * o, c[1] * o, c[2] * o, c[3] * o]
1341 }
1342
1343 for cmd in &list.commands {
1344 match cmd {
1345 Command::PushTransform(t) => {
1347 _current_transform = *t;
1349 transform_stack.push(_current_transform);
1350 }
1351 Command::PopTransform => {
1352 transform_stack.pop();
1353 _current_transform = transform_stack
1354 .last()
1355 .copied()
1356 .unwrap_or(Transform2D::identity());
1357 }
1358
1359 Command::DrawText {
1361 run, z, transform, ..
1362 } => {
1363 let final_transform = *transform;
1365 let opa = current_opacity(&opacity_stack);
1366 let mut text_run = run.clone();
1367 if opa < 0.999 {
1368 text_run.color.r *= opa;
1369 text_run.color.g *= opa;
1370 text_run.color.b *= opa;
1371 text_run.color.a *= opa;
1372 }
1373 text_draws.push(ExtractedTextDraw {
1374 run: text_run,
1375 z: *z,
1376 transform: final_transform,
1377 });
1378 }
1379
1380 Command::DrawHyperlink {
1382 hyperlink,
1383 z,
1384 transform,
1385 ..
1386 } => {
1387 let final_transform = *transform;
1389 let opa = current_opacity(&opacity_stack);
1390
1391 let mut link_color = hyperlink.color;
1393 if opa < 0.999 {
1394 link_color.r *= opa;
1395 link_color.g *= opa;
1396 link_color.b *= opa;
1397 link_color.a *= opa;
1398 }
1399 let text_run = TextRun {
1400 text: hyperlink.text.clone(),
1401 pos: hyperlink.pos,
1402 size: hyperlink.size,
1403 color: link_color,
1404 weight: hyperlink.weight,
1405 style: hyperlink.style,
1406 family: hyperlink.family.clone(),
1407 };
1408 text_draws.push(ExtractedTextDraw {
1409 run: text_run,
1410 z: *z,
1411 transform: final_transform,
1412 });
1413
1414 if hyperlink.underline {
1416 let underline_color = hyperlink.underline_color.unwrap_or(hyperlink.color);
1417 let color = premul_opa(
1418 [
1419 underline_color.r,
1420 underline_color.g,
1421 underline_color.b,
1422 underline_color.a,
1423 ],
1424 opa,
1425 );
1426
1427 let (underline_x, text_width) =
1429 if let Some(w) = hyperlink.measured_width.map(|v| v.max(0.0)) {
1430 (hyperlink.pos[0], w)
1431 } else {
1432 let trimmed = hyperlink.text.trim_end();
1433 let char_count = trimmed.chars().count() as f32;
1434 let weight_boost = ((hyperlink.weight - 400.0).max(0.0) / 500.0) * 0.08;
1435 let char_width = hyperlink.size * (0.50 + weight_boost);
1436 let mut width = char_count * char_width;
1437 let inset = hyperlink.size * 0.10;
1438 if width > inset * 2.0 {
1439 width -= inset * 2.0;
1440 }
1441 (hyperlink.pos[0] + inset, width)
1442 };
1443
1444 let underline_thickness = (hyperlink.size * 0.08).max(1.0);
1448 let underline_offset = hyperlink.size * 0.10; let underline_rect = Rect {
1451 x: underline_x,
1452 y: hyperlink.pos[1] + underline_offset,
1453 w: text_width,
1454 h: underline_thickness,
1455 };
1456
1457 let (v, i) = rect_to_verts(underline_rect, color, final_transform, *z as f32);
1458 if is_transparent(color[3]) {
1459 let index_start = transparent_indices.len();
1460 let base = transparent_vertices.len() as u16;
1461 transparent_vertices.extend_from_slice(&v);
1462 transparent_indices.extend(i.iter().map(|idx| base + idx));
1463 record_transparent_batch(
1464 &mut transparent_batches,
1465 *z,
1466 index_start,
1467 transparent_indices.len(),
1468 );
1469 } else {
1470 let base = vertices.len() as u16;
1471 vertices.extend_from_slice(&v);
1472 indices.extend(i.iter().map(|idx| base + idx));
1473 }
1474 }
1475 }
1476
1477 Command::DrawRect {
1479 rect,
1480 brush,
1481 transform,
1482 z,
1483 ..
1484 } => {
1485 let final_transform = *transform;
1487 let opa = current_opacity(&opacity_stack);
1488 match brush {
1489 Brush::Solid(col) => {
1490 let color = premul_opa([col.r, col.g, col.b, col.a], opa);
1491 let (v, i) = rect_to_verts(*rect, color, final_transform, *z as f32);
1492 if is_transparent(color[3]) {
1493 let index_start = transparent_indices.len();
1494 let base = transparent_vertices.len() as u16;
1495 transparent_vertices.extend_from_slice(&v);
1496 transparent_indices.extend(i.iter().map(|idx| base + idx));
1497 record_transparent_batch(
1498 &mut transparent_batches,
1499 *z,
1500 index_start,
1501 transparent_indices.len(),
1502 );
1503 } else {
1504 let base = vertices.len() as u16;
1505 vertices.extend_from_slice(&v);
1506 indices.extend(i.iter().map(|idx| base + idx));
1507 }
1508 }
1509 Brush::LinearGradient { stops, .. } => {
1510 let mut packed: Vec<(f32, [f32; 4])> = stops
1512 .iter()
1513 .map(|(tpos, c)| (*tpos, premul_opa([c.r, c.g, c.b, c.a], opa)))
1514 .collect();
1515 if packed.is_empty() {
1516 continue;
1517 }
1518 if packed.first().unwrap().0 > 0.0 {
1520 let c = packed.first().unwrap().1;
1521 packed.insert(0, (0.0, c));
1522 }
1523 if packed.last().unwrap().0 < 1.0 {
1524 let c = packed.last().unwrap().1;
1525 packed.push((1.0, c));
1526 }
1527 let gradient_transparent = packed.iter().any(|(_, c)| is_transparent(c[3]));
1528 if gradient_transparent {
1529 let index_start = transparent_indices.len();
1530 push_rect_linear_gradient(
1531 &mut transparent_vertices,
1532 &mut transparent_indices,
1533 *rect,
1534 &packed,
1535 final_transform,
1536 *z as f32,
1537 );
1538 record_transparent_batch(
1539 &mut transparent_batches,
1540 *z,
1541 index_start,
1542 transparent_indices.len(),
1543 );
1544 } else {
1545 push_rect_linear_gradient(
1546 &mut vertices,
1547 &mut indices,
1548 *rect,
1549 &packed,
1550 final_transform,
1551 *z as f32,
1552 );
1553 }
1554 }
1555 _ => {}
1556 }
1557 }
1558 Command::DrawRoundedRect {
1559 rrect,
1560 brush,
1561 transform,
1562 z,
1563 ..
1564 } => {
1565 let final_transform = *transform;
1566 let opa = current_opacity(&opacity_stack);
1567 match brush {
1568 Brush::Solid(col) => {
1569 let color = premul_opa([col.r, col.g, col.b, col.a], opa);
1570 if is_transparent(color[3]) {
1571 let index_start = transparent_indices.len();
1572 push_rounded_rect(
1573 &mut transparent_vertices,
1574 &mut transparent_indices,
1575 *rrect,
1576 color,
1577 *z as f32,
1578 final_transform,
1579 );
1580 record_transparent_batch(
1581 &mut transparent_batches,
1582 *z,
1583 index_start,
1584 transparent_indices.len(),
1585 );
1586 } else {
1587 push_rounded_rect(
1588 &mut vertices,
1589 &mut indices,
1590 *rrect,
1591 color,
1592 *z as f32,
1593 final_transform,
1594 );
1595 }
1596 }
1597 Brush::LinearGradient { start, end, stops } => {
1598 let packed: Vec<(f32, [f32; 4])> = stops
1599 .iter()
1600 .map(|(tpos, c)| (*tpos, premul_opa([c.r, c.g, c.b, c.a], opa)))
1601 .collect();
1602 if packed.is_empty() {
1603 continue;
1604 }
1605 let gradient_transparent = packed.iter().any(|(_, c)| is_transparent(c[3]));
1606 if gradient_transparent {
1607 let index_start = transparent_indices.len();
1608 push_rounded_rect_linear_gradient(
1609 &mut transparent_vertices,
1610 &mut transparent_indices,
1611 *rrect,
1612 *start,
1613 *end,
1614 &packed,
1615 *z as f32,
1616 final_transform,
1617 );
1618 record_transparent_batch(
1619 &mut transparent_batches,
1620 *z,
1621 index_start,
1622 transparent_indices.len(),
1623 );
1624 } else {
1625 push_rounded_rect_linear_gradient(
1626 &mut vertices,
1627 &mut indices,
1628 *rrect,
1629 *start,
1630 *end,
1631 &packed,
1632 *z as f32,
1633 final_transform,
1634 );
1635 }
1636 }
1637 Brush::RadialGradient {
1638 center,
1639 radius,
1640 stops,
1641 } => {
1642 let packed: Vec<(f32, [f32; 4])> = stops
1643 .iter()
1644 .map(|(tpos, c)| (*tpos, premul_opa([c.r, c.g, c.b, c.a], opa)))
1645 .collect();
1646 if packed.is_empty() {
1647 continue;
1648 }
1649 let gradient_transparent = packed.iter().any(|(_, c)| is_transparent(c[3]));
1650 if gradient_transparent {
1651 let index_start = transparent_indices.len();
1652 push_rounded_rect_radial_gradient(
1653 &mut transparent_vertices,
1654 &mut transparent_indices,
1655 *rrect,
1656 *center,
1657 *radius,
1658 &packed,
1659 *z as f32,
1660 final_transform,
1661 );
1662 record_transparent_batch(
1663 &mut transparent_batches,
1664 *z,
1665 index_start,
1666 transparent_indices.len(),
1667 );
1668 } else {
1669 push_rounded_rect_radial_gradient(
1670 &mut vertices,
1671 &mut indices,
1672 *rrect,
1673 *center,
1674 *radius,
1675 &packed,
1676 *z as f32,
1677 final_transform,
1678 );
1679 }
1680 }
1681 }
1682 }
1683 Command::StrokeRect {
1684 rect,
1685 stroke,
1686 brush,
1687 transform,
1688 z,
1689 ..
1690 } => {
1691 let final_transform = *transform;
1692 let opa = current_opacity(&opacity_stack);
1693 if let Brush::Solid(col) = brush {
1694 let color = premul_opa([col.r, col.g, col.b, col.a], opa);
1695 if is_transparent(color[3]) {
1696 let index_start = transparent_indices.len();
1697 push_rect_stroke(
1698 &mut transparent_vertices,
1699 &mut transparent_indices,
1700 *rect,
1701 *stroke,
1702 color,
1703 *z as f32,
1704 final_transform,
1705 );
1706 record_transparent_batch(
1707 &mut transparent_batches,
1708 *z,
1709 index_start,
1710 transparent_indices.len(),
1711 );
1712 } else {
1713 push_rect_stroke(
1714 &mut vertices,
1715 &mut indices,
1716 *rect,
1717 *stroke,
1718 color,
1719 *z as f32,
1720 final_transform,
1721 );
1722 }
1723 }
1724 }
1725 Command::StrokeRoundedRect {
1726 rrect,
1727 stroke,
1728 brush,
1729 transform,
1730 z,
1731 ..
1732 } => {
1733 let final_transform = *transform;
1734 let opa = current_opacity(&opacity_stack);
1735 if let Brush::Solid(col) = brush {
1736 let color = premul_opa([col.r, col.g, col.b, col.a], opa);
1737 if is_transparent(color[3]) {
1738 let index_start = transparent_indices.len();
1739 push_rounded_rect_stroke(
1740 &mut transparent_vertices,
1741 &mut transparent_indices,
1742 *rrect,
1743 *stroke,
1744 color,
1745 *z as f32,
1746 final_transform,
1747 );
1748 record_transparent_batch(
1749 &mut transparent_batches,
1750 *z,
1751 index_start,
1752 transparent_indices.len(),
1753 );
1754 } else {
1755 push_rounded_rect_stroke(
1756 &mut vertices,
1757 &mut indices,
1758 *rrect,
1759 *stroke,
1760 color,
1761 *z as f32,
1762 final_transform,
1763 );
1764 }
1765 }
1766 }
1767 Command::DrawEllipse {
1768 center,
1769 radii,
1770 brush,
1771 transform,
1772 z,
1773 ..
1774 } => {
1775 let final_transform = *transform;
1776 let opa = current_opacity(&opacity_stack);
1777 match brush {
1778 Brush::Solid(col) => {
1779 let color = premul_opa([col.r, col.g, col.b, col.a], opa);
1780 if is_transparent(color[3]) {
1781 let index_start = transparent_indices.len();
1782 push_ellipse(
1783 &mut transparent_vertices,
1784 &mut transparent_indices,
1785 *center,
1786 *radii,
1787 color,
1788 *z as f32,
1789 final_transform,
1790 );
1791 record_transparent_batch(
1792 &mut transparent_batches,
1793 *z,
1794 index_start,
1795 transparent_indices.len(),
1796 );
1797 } else {
1798 push_ellipse(
1799 &mut vertices,
1800 &mut indices,
1801 *center,
1802 *radii,
1803 color,
1804 *z as f32,
1805 final_transform,
1806 );
1807 }
1808 }
1809 Brush::RadialGradient {
1810 center: _gcenter,
1811 radius: _r,
1812 stops,
1813 } => {
1814 let mut packed: Vec<(f32, [f32; 4])> = stops
1815 .iter()
1816 .map(|(t, c)| (*t, premul_opa([c.r, c.g, c.b, c.a], opa)))
1817 .collect();
1818 if packed.is_empty() {
1819 continue;
1820 }
1821 if packed.first().unwrap().0 > 0.0 {
1822 let c = packed.first().unwrap().1;
1823 packed.insert(0, (0.0, c));
1824 }
1825 if packed.last().unwrap().0 < 1.0 {
1826 let c = packed.last().unwrap().1;
1827 packed.push((1.0, c));
1828 }
1829 let gradient_transparent = packed.iter().any(|(_, c)| is_transparent(c[3]));
1830 if gradient_transparent {
1831 let index_start = transparent_indices.len();
1832 push_ellipse_radial_gradient(
1833 &mut transparent_vertices,
1834 &mut transparent_indices,
1835 *center,
1836 *radii,
1837 &packed,
1838 *z as f32,
1839 final_transform,
1840 );
1841 record_transparent_batch(
1842 &mut transparent_batches,
1843 *z,
1844 index_start,
1845 transparent_indices.len(),
1846 );
1847 } else {
1848 push_ellipse_radial_gradient(
1849 &mut vertices,
1850 &mut indices,
1851 *center,
1852 *radii,
1853 &packed,
1854 *z as f32,
1855 final_transform,
1856 );
1857 }
1858 }
1859 _ => {}
1860 }
1861 }
1862 Command::FillPath {
1863 path,
1864 color,
1865 transform,
1866 z,
1867 ..
1868 } => {
1869 let final_transform = *transform;
1870 let opa = current_opacity(&opacity_stack);
1871 let col = premul_opa([color.r, color.g, color.b, color.a], opa);
1872 if is_transparent(col[3]) {
1873 let index_start = transparent_indices.len();
1874 tessellate_path_fill(
1875 &mut transparent_vertices,
1876 &mut transparent_indices,
1877 path,
1878 col,
1879 *z as f32,
1880 final_transform,
1881 );
1882 record_transparent_batch(
1883 &mut transparent_batches,
1884 *z,
1885 index_start,
1886 transparent_indices.len(),
1887 );
1888 } else {
1889 tessellate_path_fill(
1890 &mut vertices,
1891 &mut indices,
1892 path,
1893 col,
1894 *z as f32,
1895 final_transform,
1896 );
1897 }
1898 }
1899 Command::StrokePath {
1900 path,
1901 stroke,
1902 color,
1903 transform,
1904 z,
1905 ..
1906 } => {
1907 let final_transform = *transform;
1908 let opa = current_opacity(&opacity_stack);
1909 let col = premul_opa([color.r, color.g, color.b, color.a], opa);
1910 if is_transparent(col[3]) {
1911 let index_start = transparent_indices.len();
1912 tessellate_path_stroke(
1913 &mut transparent_vertices,
1914 &mut transparent_indices,
1915 path,
1916 *stroke,
1917 col,
1918 *z as f32,
1919 final_transform,
1920 );
1921 record_transparent_batch(
1922 &mut transparent_batches,
1923 *z,
1924 index_start,
1925 transparent_indices.len(),
1926 );
1927 } else {
1928 tessellate_path_stroke(
1929 &mut vertices,
1930 &mut indices,
1931 path,
1932 *stroke,
1933 col,
1934 *z as f32,
1935 final_transform,
1936 );
1937 }
1938 }
1939 Command::DrawImage {
1940 path,
1941 origin,
1942 size,
1943 z,
1944 transform,
1945 } => {
1946 let final_transform = *transform;
1948 let world_origin = apply_transform(*origin, final_transform);
1949 let opa = current_opacity(&opacity_stack);
1950 image_draws.push(ExtractedImageDraw {
1951 path: path.clone(),
1952 origin: world_origin,
1953 size: *size,
1954 z: *z,
1955 transform: final_transform,
1956 opacity: opa,
1957 });
1958 }
1959 Command::DrawSvg {
1960 path,
1961 origin,
1962 max_size,
1963 z,
1964 transform,
1965 } => {
1966 let final_transform = *transform;
1968 let world_origin = apply_transform(*origin, final_transform);
1969 let opa = current_opacity(&opacity_stack);
1970 svg_draws.push(ExtractedSvgDraw {
1971 path: path.clone(),
1972 origin: world_origin,
1973 size: *max_size,
1974 z: *z,
1975 transform: final_transform,
1976 opacity: opa,
1977 });
1978 }
1979 Command::DrawExternalTexture {
1980 rect,
1981 texture_id,
1982 z,
1983 transform,
1984 opacity,
1985 premultiplied,
1986 } => {
1987 let final_transform = *transform;
1988 let world_origin = apply_transform([rect.x, rect.y], final_transform);
1989 let opa = current_opacity(&opacity_stack);
1990 external_texture_draws.push(ExtractedExternalTextureDraw {
1991 texture_id: *texture_id,
1992 origin: world_origin,
1993 size: [rect.w, rect.h],
1994 z: *z,
1995 opacity: *opacity * opa,
1996 premultiplied: *premultiplied,
1997 });
1998 }
1999 Command::BoxShadow { .. } => {}
2001 Command::HitRegionRect { .. } => {}
2003 Command::HitRegionRoundedRect { .. } => {}
2004 Command::HitRegionEllipse { .. } => {}
2005 Command::PushClip(_) => {}
2007 Command::PopClip => {}
2008 Command::PushOpacity(alpha) => {
2009 let parent = current_opacity(&opacity_stack);
2010 opacity_stack.push(parent * alpha.clamp(0.0, 1.0));
2011 }
2012 Command::PopOpacity => {
2013 if opacity_stack.len() > 1 {
2014 opacity_stack.pop();
2015 }
2016 }
2017 }
2018 }
2019
2020 let align_indices = |indices: &mut Vec<u16>| {
2022 if (indices.len() % 2) != 0 {
2023 if indices.len() >= 3 {
2024 let a = indices[indices.len() - 3];
2025 let b = indices[indices.len() - 2];
2026 let c = indices[indices.len() - 1];
2027 indices.extend_from_slice(&[a, b, c]);
2028 } else {
2029 indices.push(0);
2030 }
2031 }
2032 };
2033 align_indices(&mut indices);
2034 align_indices(&mut transparent_indices);
2035
2036 let upload_scene = |allocator: &mut RenderAllocator,
2038 queue: &wgpu::Queue,
2039 vertices: &[Vertex],
2040 indices: &[u16]|
2041 -> GpuScene {
2042 let vsize = (vertices.len() * std::mem::size_of::<Vertex>()) as u64;
2043 let isize = (indices.len() * std::mem::size_of::<u16>()) as u64;
2044 let vbuf = allocator.allocate_buffer(BufKey {
2045 size: vsize.max(4),
2046 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
2047 });
2048 let ibuf = allocator.allocate_buffer(BufKey {
2049 size: isize.max(4),
2050 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
2051 });
2052 if vsize > 0 {
2053 queue.write_buffer(&vbuf.buffer, 0, bytemuck::cast_slice(vertices));
2054 }
2055 if isize > 0 {
2056 queue.write_buffer(&ibuf.buffer, 0, bytemuck::cast_slice(indices));
2057 }
2058 GpuScene {
2059 vertex: vbuf,
2060 index: ibuf,
2061 vertices: vertices.len() as u32,
2062 indices: indices.len() as u32,
2063 }
2064 };
2065 let gpu_scene = upload_scene(allocator, queue, &vertices, &indices);
2066 let transparent_gpu_scene = upload_scene(
2067 allocator,
2068 queue,
2069 &transparent_vertices,
2070 &transparent_indices,
2071 );
2072
2073 Ok(UnifiedSceneData {
2074 gpu_scene,
2075 transparent_gpu_scene,
2076 transparent_batches,
2077 text_draws,
2078 image_draws,
2079 svg_draws,
2080 external_texture_draws,
2081 })
2082}