1#![cfg_attr(windows, allow(dead_code))]
3
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use crate::{AtlasTextureId, AtlasTile, CustomRenderPrimitive, bounds_tree::BoundsTree};
8use gpui_types::{
9 Background, Bounds, ContentMask, Corners, DrawOrder, Edges, Hsla, Pixels, Point, Radians,
10 ScaledPixels, Shadow, Size, point,
11};
12use std::{
13 fmt::Debug,
14 iter::Peekable,
15 ops::{Add, Range, Sub},
16 slice,
17};
18
19#[allow(non_camel_case_types, unused)]
20#[expect(missing_docs)]
21pub type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
22
23#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
28#[repr(transparent)]
29pub struct PaddedBool32(u32);
30
31impl From<bool> for PaddedBool32 {
32 fn from(value: bool) -> Self {
33 PaddedBool32(value as u32)
34 }
35}
36
37#[derive(Default)]
38#[expect(missing_docs)]
39pub struct Scene {
40 pub(crate) paint_operations: Vec<PaintOperation>,
41 primitive_bounds: BoundsTree<ScaledPixels>,
42 layer_stack: Vec<DrawOrder>,
43 pub shadows: Vec<Shadow>,
44 pub quads: Vec<Quad>,
45 pub paths: Vec<Path<ScaledPixels>>,
46 pub underlines: Vec<Underline>,
47 pub monochrome_sprites: Vec<MonochromeSprite>,
48 pub subpixel_sprites: Vec<SubpixelSprite>,
49 pub polychrome_sprites: Vec<PolychromeSprite>,
50 pub surfaces: Vec<PaintSurface>,
51 pub custom: Vec<CustomRenderPrimitive>,
52}
53
54#[expect(missing_docs)]
55impl Scene {
56 pub fn clear(&mut self) {
57 self.paint_operations.clear();
58 self.primitive_bounds.clear();
59 self.layer_stack.clear();
60 self.paths.clear();
61 self.shadows.clear();
62 self.quads.clear();
63 self.underlines.clear();
64 self.monochrome_sprites.clear();
65 self.subpixel_sprites.clear();
66 self.polychrome_sprites.clear();
67 self.surfaces.clear();
68 self.custom.clear();
69 }
70
71 pub fn len(&self) -> usize {
72 self.paint_operations.len()
73 }
74
75 pub fn push_layer(&mut self, bounds: Bounds<ScaledPixels>) {
76 let order = self.primitive_bounds.insert(bounds);
77 self.layer_stack.push(order);
78 self.paint_operations
79 .push(PaintOperation::StartLayer(bounds));
80 }
81
82 pub fn pop_layer(&mut self) {
83 self.layer_stack.pop();
84 self.paint_operations.push(PaintOperation::EndLayer);
85 }
86
87 pub fn insert_primitive(&mut self, primitive: impl Into<Primitive>) {
88 let mut primitive = primitive.into();
89 let clipped_bounds = primitive
90 .bounds()
91 .intersect(&primitive.content_mask().bounds);
92
93 if clipped_bounds.is_empty() {
94 return;
95 }
96
97 let order = self
98 .layer_stack
99 .last()
100 .copied()
101 .unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds));
102 match &mut primitive {
103 Primitive::Shadow(shadow) => {
104 shadow.order = order;
105 self.shadows.push(*shadow);
106 }
107 Primitive::Quad(quad) => {
108 quad.order = order;
109 self.quads.push(*quad);
110 }
111 Primitive::Path(path) => {
112 path.order = order;
113 path.id = PathId(self.paths.len());
114 self.paths.push(path.clone());
115 }
116 Primitive::Underline(underline) => {
117 underline.order = order;
118 self.underlines.push(*underline);
119 }
120 Primitive::MonochromeSprite(sprite) => {
121 sprite.order = order;
122 self.monochrome_sprites.push(*sprite);
123 }
124 Primitive::SubpixelSprite(sprite) => {
125 sprite.order = order;
126 self.subpixel_sprites.push(*sprite);
127 }
128 Primitive::PolychromeSprite(sprite) => {
129 sprite.order = order;
130 self.polychrome_sprites.push(*sprite);
131 }
132 Primitive::Surface(surface) => {
133 #[allow(unreachable_code)]
137 {
138 surface.order = order;
139 self.surfaces.push(surface.clone());
140 }
141 }
142 Primitive::Custom(custom) => {
143 *custom.order_mut() = order;
144 self.custom.push(custom.clone());
145 }
146 }
147 self.paint_operations
148 .push(PaintOperation::Primitive(primitive));
149 }
150
151 pub fn replay(&mut self, range: Range<usize>, prev_scene: &Scene) {
152 for operation in &prev_scene.paint_operations[range] {
153 match operation {
154 PaintOperation::Primitive(primitive) => self.insert_primitive(primitive.clone()),
155 PaintOperation::StartLayer(bounds) => self.push_layer(*bounds),
156 PaintOperation::EndLayer => self.pop_layer(),
157 }
158 }
159 }
160
161 pub fn finish(&mut self) {
162 self.shadows.sort_by_key(|shadow| shadow.order);
163 self.quads.sort_by_key(|quad| quad.order);
164 self.paths.sort_by_key(|path| path.order);
165 self.underlines.sort_by_key(|underline| underline.order);
166 self.monochrome_sprites
167 .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
168 self.subpixel_sprites
169 .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
170 self.polychrome_sprites
171 .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
172 self.surfaces.sort_by_key(|surface| surface.order);
173 self.custom.sort_by_key(|custom| custom.order());
174 }
175
176 #[cfg_attr(
177 all(
178 any(target_os = "linux", target_os = "freebsd"),
179 not(any(feature = "x11", feature = "wayland"))
180 ),
181 allow(dead_code)
182 )]
183 pub fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> + '_ {
184 BatchIterator {
185 shadows_start: 0,
186 shadows_iter: self.shadows.iter().peekable(),
187 quads_start: 0,
188 quads_iter: self.quads.iter().peekable(),
189 paths_start: 0,
190 paths_iter: self.paths.iter().peekable(),
191 underlines_start: 0,
192 underlines_iter: self.underlines.iter().peekable(),
193 monochrome_sprites_start: 0,
194 monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
195 subpixel_sprites_start: 0,
196 subpixel_sprites_iter: self.subpixel_sprites.iter().peekable(),
197 polychrome_sprites_start: 0,
198 polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
199 surfaces_start: 0,
200 surfaces_iter: self.surfaces.iter().peekable(),
201 custom_start: 0,
202 custom_iter: self.custom.iter().peekable(),
203 }
204 }
205}
206
207#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
208#[cfg_attr(
209 all(
210 any(target_os = "linux", target_os = "freebsd"),
211 not(any(feature = "x11", feature = "wayland"))
212 ),
213 allow(dead_code)
214)]
215pub(crate) enum PrimitiveKind {
216 Shadow,
217 #[default]
218 Quad,
219 Path,
220 Underline,
221 MonochromeSprite,
222 SubpixelSprite,
223 PolychromeSprite,
224 Surface,
225 Custom,
226}
227
228pub(crate) enum PaintOperation {
229 Primitive(Primitive),
230 StartLayer(Bounds<ScaledPixels>),
231 EndLayer,
232}
233
234#[derive(Clone)]
235#[expect(missing_docs)]
236pub enum Primitive {
237 Shadow(Shadow),
238 Quad(Quad),
239 Path(Path<ScaledPixels>),
240 Underline(Underline),
241 MonochromeSprite(MonochromeSprite),
242 SubpixelSprite(SubpixelSprite),
243 PolychromeSprite(PolychromeSprite),
244 Surface(PaintSurface),
245 Custom(CustomRenderPrimitive),
246}
247
248#[expect(missing_docs)]
249impl Primitive {
250 pub fn bounds(&self) -> &Bounds<ScaledPixels> {
251 match self {
252 Primitive::Shadow(shadow) => &shadow.bounds,
253 Primitive::Quad(quad) => &quad.bounds,
254 Primitive::Path(path) => &path.bounds,
255 Primitive::Underline(underline) => &underline.bounds,
256 Primitive::MonochromeSprite(sprite) => &sprite.bounds,
257 Primitive::SubpixelSprite(sprite) => &sprite.bounds,
258 Primitive::PolychromeSprite(sprite) => &sprite.bounds,
259 Primitive::Surface(surface) => &surface.bounds,
260 Primitive::Custom(custom) => custom.bounds(),
261 }
262 }
263
264 pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
265 match self {
266 Primitive::Shadow(shadow) => &shadow.content_mask,
267 Primitive::Quad(quad) => &quad.content_mask,
268 Primitive::Path(path) => &path.content_mask,
269 Primitive::Underline(underline) => &underline.content_mask,
270 Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
271 Primitive::SubpixelSprite(sprite) => &sprite.content_mask,
272 Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
273 Primitive::Surface(surface) => &surface.content_mask,
274 Primitive::Custom(custom) => custom.content_mask(),
275 }
276 }
277}
278
279#[cfg_attr(
280 all(
281 any(target_os = "linux", target_os = "freebsd"),
282 not(any(feature = "x11", feature = "wayland"))
283 ),
284 allow(dead_code)
285)]
286struct BatchIterator<'a> {
287 shadows_start: usize,
288 shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
289 quads_start: usize,
290 quads_iter: Peekable<slice::Iter<'a, Quad>>,
291 paths_start: usize,
292 paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
293 underlines_start: usize,
294 underlines_iter: Peekable<slice::Iter<'a, Underline>>,
295 monochrome_sprites_start: usize,
296 monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
297 subpixel_sprites_start: usize,
298 subpixel_sprites_iter: Peekable<slice::Iter<'a, SubpixelSprite>>,
299 polychrome_sprites_start: usize,
300 polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
301 surfaces_start: usize,
302 surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
303 custom_start: usize,
304 custom_iter: Peekable<slice::Iter<'a, CustomRenderPrimitive>>,
305}
306
307impl<'a> Iterator for BatchIterator<'a> {
308 type Item = PrimitiveBatch;
309
310 fn next(&mut self) -> Option<Self::Item> {
311 let mut orders_and_kinds = [
312 (
313 self.shadows_iter.peek().map(|s| s.order),
314 PrimitiveKind::Shadow,
315 ),
316 (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
317 (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
318 (
319 self.underlines_iter.peek().map(|u| u.order),
320 PrimitiveKind::Underline,
321 ),
322 (
323 self.monochrome_sprites_iter.peek().map(|s| s.order),
324 PrimitiveKind::MonochromeSprite,
325 ),
326 (
327 self.subpixel_sprites_iter.peek().map(|s| s.order),
328 PrimitiveKind::SubpixelSprite,
329 ),
330 (
331 self.polychrome_sprites_iter.peek().map(|s| s.order),
332 PrimitiveKind::PolychromeSprite,
333 ),
334 (
335 self.surfaces_iter.peek().map(|s| s.order),
336 PrimitiveKind::Surface,
337 ),
338 (
339 self.custom_iter.peek().map(|c| c.order()),
340 PrimitiveKind::Custom,
341 ),
342 ];
343 orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
344
345 let first = orders_and_kinds[0];
346 let second = orders_and_kinds[1];
347 let (batch_kind, max_order_and_kind) = if first.0.is_some() {
348 (first.1, (second.0.unwrap_or(u32::MAX), second.1))
349 } else {
350 return None;
351 };
352
353 match batch_kind {
354 PrimitiveKind::Shadow => {
355 let shadows_start = self.shadows_start;
356 let mut shadows_end = shadows_start + 1;
357 self.shadows_iter.next();
358 while self
359 .shadows_iter
360 .next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
361 .is_some()
362 {
363 shadows_end += 1;
364 }
365 self.shadows_start = shadows_end;
366 Some(PrimitiveBatch::Shadows(shadows_start..shadows_end))
367 }
368 PrimitiveKind::Quad => {
369 let quads_start = self.quads_start;
370 let mut quads_end = quads_start + 1;
371 self.quads_iter.next();
372 while self
373 .quads_iter
374 .next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
375 .is_some()
376 {
377 quads_end += 1;
378 }
379 self.quads_start = quads_end;
380 Some(PrimitiveBatch::Quads(quads_start..quads_end))
381 }
382 PrimitiveKind::Path => {
383 let paths_start = self.paths_start;
384 let mut paths_end = paths_start + 1;
385 self.paths_iter.next();
386 while self
387 .paths_iter
388 .next_if(|path| (path.order, batch_kind) < max_order_and_kind)
389 .is_some()
390 {
391 paths_end += 1;
392 }
393 self.paths_start = paths_end;
394 Some(PrimitiveBatch::Paths(paths_start..paths_end))
395 }
396 PrimitiveKind::Underline => {
397 let underlines_start = self.underlines_start;
398 let mut underlines_end = underlines_start + 1;
399 self.underlines_iter.next();
400 while self
401 .underlines_iter
402 .next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
403 .is_some()
404 {
405 underlines_end += 1;
406 }
407 self.underlines_start = underlines_end;
408 Some(PrimitiveBatch::Underlines(underlines_start..underlines_end))
409 }
410 PrimitiveKind::MonochromeSprite => {
411 let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
412 let sprites_start = self.monochrome_sprites_start;
413 let mut sprites_end = sprites_start + 1;
414 self.monochrome_sprites_iter.next();
415 while self
416 .monochrome_sprites_iter
417 .next_if(|sprite| {
418 (sprite.order, batch_kind) < max_order_and_kind
419 && sprite.tile.texture_id == texture_id
420 })
421 .is_some()
422 {
423 sprites_end += 1;
424 }
425 self.monochrome_sprites_start = sprites_end;
426 Some(PrimitiveBatch::MonochromeSprites {
427 texture_id,
428 range: sprites_start..sprites_end,
429 })
430 }
431 PrimitiveKind::SubpixelSprite => {
432 let texture_id = self.subpixel_sprites_iter.peek().unwrap().tile.texture_id;
433 let sprites_start = self.subpixel_sprites_start;
434 let mut sprites_end = sprites_start + 1;
435 self.subpixel_sprites_iter.next();
436 while self
437 .subpixel_sprites_iter
438 .next_if(|sprite| {
439 (sprite.order, batch_kind) < max_order_and_kind
440 && sprite.tile.texture_id == texture_id
441 })
442 .is_some()
443 {
444 sprites_end += 1;
445 }
446 self.subpixel_sprites_start = sprites_end;
447 Some(PrimitiveBatch::SubpixelSprites {
448 texture_id,
449 range: sprites_start..sprites_end,
450 })
451 }
452 PrimitiveKind::PolychromeSprite => {
453 let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
454 let sprites_start = self.polychrome_sprites_start;
455 let mut sprites_end = sprites_start + 1;
456 self.polychrome_sprites_iter.next();
457 while self
458 .polychrome_sprites_iter
459 .next_if(|sprite| {
460 (sprite.order, batch_kind) < max_order_and_kind
461 && sprite.tile.texture_id == texture_id
462 })
463 .is_some()
464 {
465 sprites_end += 1;
466 }
467 self.polychrome_sprites_start = sprites_end;
468 Some(PrimitiveBatch::PolychromeSprites {
469 texture_id,
470 range: sprites_start..sprites_end,
471 })
472 }
473 PrimitiveKind::Surface => {
474 let surfaces_start = self.surfaces_start;
475 let mut surfaces_end = surfaces_start + 1;
476 self.surfaces_iter.next();
477 while self
478 .surfaces_iter
479 .next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
480 .is_some()
481 {
482 surfaces_end += 1;
483 }
484 self.surfaces_start = surfaces_end;
485 Some(PrimitiveBatch::Surfaces(surfaces_start..surfaces_end))
486 }
487 PrimitiveKind::Custom => {
488 let custom_start = self.custom_start;
489 let mut custom_end = custom_start + 1;
490 self.custom_iter.next();
491 while self
492 .custom_iter
493 .next_if(|custom| (custom.order(), batch_kind) < max_order_and_kind)
494 .is_some()
495 {
496 custom_end += 1;
497 }
498 self.custom_start = custom_end;
499 Some(PrimitiveBatch::Custom(custom_start..custom_end))
500 }
501 }
502 }
503}
504
505#[derive(Debug)]
506#[cfg_attr(
507 all(
508 any(target_os = "linux", target_os = "freebsd"),
509 not(any(feature = "x11", feature = "wayland"))
510 ),
511 allow(dead_code)
512)]
513#[allow(missing_docs)]
514pub enum PrimitiveBatch {
515 Shadows(Range<usize>),
516 Quads(Range<usize>),
517 Paths(Range<usize>),
518 Underlines(Range<usize>),
519 MonochromeSprites {
520 texture_id: AtlasTextureId,
521 range: Range<usize>,
522 },
523 #[cfg_attr(target_os = "macos", allow(dead_code))]
524 SubpixelSprites {
525 texture_id: AtlasTextureId,
526 range: Range<usize>,
527 },
528 PolychromeSprites {
529 texture_id: AtlasTextureId,
530 range: Range<usize>,
531 },
532 Surfaces(Range<usize>),
533 Custom(Range<usize>),
534}
535
536impl PrimitiveBatch {
537 #[expect(missing_docs)]
538 pub fn label(&self) -> String {
539 match self {
540 Self::Shadows(range) => format!("shadows ({})", range.len()),
541 Self::Quads(range) => format!("quads ({})", range.len()),
542 Self::Paths(range) => format!("paths ({})", range.len()),
543 Self::Underlines(range) => format!("underlines ({})", range.len()),
544 Self::MonochromeSprites { texture_id, range } => {
545 format!(
546 "monochrome sprites ({}) on atlas {}",
547 range.len(),
548 texture_id.index
549 )
550 }
551 Self::SubpixelSprites { texture_id, range } => {
552 format!(
553 "subpixel sprites ({}) on atlas {}",
554 range.len(),
555 texture_id.index
556 )
557 }
558 Self::PolychromeSprites { texture_id, range } => {
559 format!(
560 "polychrome sprites ({}) on atlas {}",
561 range.len(),
562 texture_id.index
563 )
564 }
565 Self::Surfaces(range) => format!("surfaces ({})", range.len()),
566 Self::Custom(range) => format!("custom ({})", range.len()),
567 }
568 }
569}
570
571#[derive(Default, Debug, Copy, Clone)]
572#[repr(C)]
573#[expect(missing_docs)]
574pub struct Quad {
575 pub order: DrawOrder,
576 pub border_style: BorderStyle,
577 pub bounds: Bounds<ScaledPixels>,
578 pub content_mask: ContentMask<ScaledPixels>,
579 pub background: Background,
580 pub border_color: Hsla,
581 pub corner_radii: Corners<ScaledPixels>,
582 pub border_widths: Edges<ScaledPixels>,
583}
584
585impl From<Quad> for Primitive {
586 fn from(quad: Quad) -> Self {
587 Primitive::Quad(quad)
588 }
589}
590
591#[derive(Debug, Copy, Clone)]
592#[repr(C)]
593#[expect(missing_docs)]
594pub struct Underline {
595 pub order: DrawOrder,
596 pub pad: u32, pub bounds: Bounds<ScaledPixels>,
598 pub content_mask: ContentMask<ScaledPixels>,
599 pub color: Hsla,
600 pub thickness: ScaledPixels,
601 pub wavy: PaddedBool32,
602}
603
604impl From<Underline> for Primitive {
605 fn from(underline: Underline) -> Self {
606 Primitive::Underline(underline)
607 }
608}
609
610impl From<Shadow> for Primitive {
611 fn from(shadow: Shadow) -> Self {
612 Primitive::Shadow(shadow)
613 }
614}
615
616#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
618#[repr(C)]
619pub enum BorderStyle {
620 #[default]
622 Solid = 0,
623 Dashed = 1,
625}
626
627#[derive(Debug, Clone, Copy, PartialEq)]
629#[repr(C)]
630pub struct TransformationMatrix {
631 pub rotation_scale: [[f32; 2]; 2],
634 pub translation: [f32; 2],
636}
637
638impl Eq for TransformationMatrix {}
639
640impl TransformationMatrix {
641 pub fn unit() -> Self {
643 Self {
644 rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
645 translation: [0.0, 0.0],
646 }
647 }
648
649 pub fn translate(self, point: Point<ScaledPixels>) -> Self {
651 self.compose(Self {
652 rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
653 translation: [point.x.0, point.y.0],
654 })
655 }
656
657 pub fn rotate(self, angle: Radians) -> Self {
659 self.compose(Self {
660 rotation_scale: [
661 [angle.0.cos(), -angle.0.sin()],
662 [angle.0.sin(), angle.0.cos()],
663 ],
664 translation: [0.0, 0.0],
665 })
666 }
667
668 pub fn scale(self, size: Size<f32>) -> Self {
670 self.compose(Self {
671 rotation_scale: [[size.width, 0.0], [0.0, size.height]],
672 translation: [0.0, 0.0],
673 })
674 }
675
676 #[inline]
680 pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
681 if other == Self::unit() {
682 return self;
683 }
684 TransformationMatrix {
686 rotation_scale: [
687 [
688 self.rotation_scale[0][0] * other.rotation_scale[0][0]
689 + self.rotation_scale[0][1] * other.rotation_scale[1][0],
690 self.rotation_scale[0][0] * other.rotation_scale[0][1]
691 + self.rotation_scale[0][1] * other.rotation_scale[1][1],
692 ],
693 [
694 self.rotation_scale[1][0] * other.rotation_scale[0][0]
695 + self.rotation_scale[1][1] * other.rotation_scale[1][0],
696 self.rotation_scale[1][0] * other.rotation_scale[0][1]
697 + self.rotation_scale[1][1] * other.rotation_scale[1][1],
698 ],
699 ],
700 translation: [
701 self.translation[0]
702 + self.rotation_scale[0][0] * other.translation[0]
703 + self.rotation_scale[0][1] * other.translation[1],
704 self.translation[1]
705 + self.rotation_scale[1][0] * other.translation[0]
706 + self.rotation_scale[1][1] * other.translation[1],
707 ],
708 }
709 }
710
711 pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
713 let input = [point.x.0, point.y.0];
714 let mut output = self.translation;
715 for (i, output_cell) in output.iter_mut().enumerate() {
716 for (k, input_cell) in input.iter().enumerate() {
717 *output_cell += self.rotation_scale[i][k] * *input_cell;
718 }
719 }
720 Point::new(output[0].into(), output[1].into())
721 }
722}
723
724impl Default for TransformationMatrix {
725 fn default() -> Self {
726 Self::unit()
727 }
728}
729
730#[derive(Copy, Clone, Debug)]
731#[repr(C)]
732#[expect(missing_docs)]
733pub struct MonochromeSprite {
734 pub order: DrawOrder,
735 pub pad: u32,
736 pub bounds: Bounds<ScaledPixels>,
737 pub content_mask: ContentMask<ScaledPixels>,
738 pub color: Hsla,
739 pub tile: AtlasTile,
740 pub transformation: TransformationMatrix,
741}
742
743impl From<MonochromeSprite> for Primitive {
744 fn from(sprite: MonochromeSprite) -> Self {
745 Primitive::MonochromeSprite(sprite)
746 }
747}
748
749#[derive(Copy, Clone, Debug)]
750#[repr(C)]
751#[expect(missing_docs)]
752pub struct SubpixelSprite {
753 pub order: DrawOrder,
754 pub pad: u32, pub bounds: Bounds<ScaledPixels>,
756 pub content_mask: ContentMask<ScaledPixels>,
757 pub color: Hsla,
758 pub tile: AtlasTile,
759 pub transformation: TransformationMatrix,
760}
761
762impl From<SubpixelSprite> for Primitive {
763 fn from(sprite: SubpixelSprite) -> Self {
764 Primitive::SubpixelSprite(sprite)
765 }
766}
767
768#[derive(Copy, Clone, Debug)]
769#[repr(C)]
770#[expect(missing_docs)]
771pub struct PolychromeSprite {
772 pub order: DrawOrder,
773 pub pad: u32,
774 pub grayscale: PaddedBool32,
775 pub opacity: f32,
776 pub bounds: Bounds<ScaledPixels>,
777 pub content_mask: ContentMask<ScaledPixels>,
778 pub corner_radii: Corners<ScaledPixels>,
779 pub tile: AtlasTile,
780}
781
782impl From<PolychromeSprite> for Primitive {
783 fn from(sprite: PolychromeSprite) -> Self {
784 Primitive::PolychromeSprite(sprite)
785 }
786}
787
788#[cfg(target_os = "windows")]
800#[derive(Clone, Debug, PartialEq, Eq)]
801#[allow(missing_docs)]
802pub enum DirectXSource {
803 Texture(windows::Win32::Graphics::Direct3D11::ID3D11Texture2D),
805 View(windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView),
807 Shared(SharedDirectXSurface),
809}
810
811#[cfg(target_os = "windows")]
819#[derive(Clone, Debug, PartialEq, Eq)]
820pub struct SharedDirectXSurface {
821 pub texture: windows::Win32::Foundation::HANDLE,
823 pub fence: Option<SharedDirectXFence>,
826 pub width: u32,
828 pub height: u32,
830}
831
832#[cfg(target_os = "windows")]
834#[derive(Clone, Debug, PartialEq, Eq)]
835pub struct SharedDirectXFence {
836 pub handle: windows::Win32::Foundation::HANDLE,
838 pub value: u64,
840}
841
842#[derive(Clone, Debug, PartialEq, Eq)]
850#[allow(missing_docs)]
851pub enum SurfaceSource {
852 #[cfg(target_os = "macos")]
854 CoreVideo(core_video::pixel_buffer::CVPixelBuffer),
855 #[cfg(target_os = "windows")]
858 DirectX(DirectXSource),
859 #[cfg(target_os = "linux")]
861 DmaBuf(crate::dmabuf::DmaBufHandle),
862}
863
864#[cfg(target_os = "macos")]
865impl From<core_video::pixel_buffer::CVPixelBuffer> for SurfaceSource {
866 fn from(image_buffer: core_video::pixel_buffer::CVPixelBuffer) -> Self {
867 SurfaceSource::CoreVideo(image_buffer)
868 }
869}
870
871#[cfg(target_os = "windows")]
872impl From<windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView> for DirectXSource {
873 fn from(view: windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView) -> Self {
874 DirectXSource::View(view)
875 }
876}
877
878#[cfg(target_os = "windows")]
879impl From<windows::Win32::Graphics::Direct3D11::ID3D11Texture2D> for DirectXSource {
880 fn from(texture: windows::Win32::Graphics::Direct3D11::ID3D11Texture2D) -> Self {
881 DirectXSource::Texture(texture)
882 }
883}
884
885#[cfg(target_os = "windows")]
886impl From<windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView> for SurfaceSource {
887 fn from(view: windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView) -> Self {
888 SurfaceSource::DirectX(view.into())
889 }
890}
891
892#[cfg(target_os = "windows")]
893impl From<windows::Win32::Graphics::Direct3D11::ID3D11Texture2D> for SurfaceSource {
894 fn from(texture: windows::Win32::Graphics::Direct3D11::ID3D11Texture2D) -> Self {
895 SurfaceSource::DirectX(texture.into())
896 }
897}
898
899#[cfg(target_os = "linux")]
900impl From<crate::dmabuf::DmaBufHandle> for SurfaceSource {
901 fn from(handle: crate::dmabuf::DmaBufHandle) -> Self {
902 SurfaceSource::DmaBuf(handle)
903 }
904}
905
906#[derive(Clone, Debug)]
907#[allow(missing_docs)]
908pub struct PaintSurface {
909 pub order: DrawOrder,
910 pub bounds: Bounds<ScaledPixels>,
911 pub content_mask: ContentMask<ScaledPixels>,
912 pub source: SurfaceSource,
914}
915
916impl PaintSurface {
917 pub fn to_quad_record(&self) -> Quad {
925 let mut background = Background::default();
926 background.solid = Hsla {
927 h: 0.0,
928 s: 0.0,
929 l: 0.0,
930 a: 1.0,
931 };
932
933 Quad {
934 order: self.order,
935 border_style: BorderStyle::Solid,
936 bounds: self.bounds,
937 content_mask: self.content_mask,
938 background,
939 border_color: Hsla::default(),
940 corner_radii: Corners::default(),
941 border_widths: Edges::default(),
942 }
943 }
944}
945
946impl From<PaintSurface> for Primitive {
947 fn from(surface: PaintSurface) -> Self {
948 Primitive::Surface(surface)
949 }
950}
951
952impl From<CustomRenderPrimitive> for Primitive {
953 fn from(custom: CustomRenderPrimitive) -> Self {
954 Primitive::Custom(custom)
955 }
956}
957
958#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
959#[expect(missing_docs)]
960pub struct PathId(pub usize);
961
962#[derive(Clone, Debug)]
964#[expect(missing_docs)]
965pub struct Path<P: Clone + Debug + Default + PartialEq> {
966 pub id: PathId,
967 pub order: DrawOrder,
968 pub bounds: Bounds<P>,
969 pub content_mask: ContentMask<P>,
970 pub vertices: Vec<PathVertex<P>>,
971 pub color: Background,
972 start: Point<P>,
973 current: Point<P>,
974 contour_count: usize,
975}
976
977impl Path<Pixels> {
978 pub fn new(start: Point<Pixels>) -> Self {
980 Self {
981 id: PathId(0),
982 order: DrawOrder::default(),
983 vertices: Vec::new(),
984 start,
985 current: start,
986 bounds: Bounds {
987 origin: start,
988 size: Default::default(),
989 },
990 content_mask: Default::default(),
991 color: Default::default(),
992 contour_count: 0,
993 }
994 }
995
996 pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
998 Path {
999 id: self.id,
1000 order: self.order,
1001 bounds: self.bounds.scale(factor),
1002 content_mask: self.content_mask.scale(factor),
1003 vertices: self
1004 .vertices
1005 .iter()
1006 .map(|vertex| vertex.scale(factor))
1007 .collect(),
1008 start: self.start.map(|start| start.scale(factor)),
1009 current: self.current.scale(factor),
1010 contour_count: self.contour_count,
1011 color: self.color,
1012 }
1013 }
1014
1015 pub fn move_to(&mut self, to: Point<Pixels>) {
1017 self.contour_count += 1;
1018 self.start = to;
1019 self.current = to;
1020 }
1021
1022 pub fn line_to(&mut self, to: Point<Pixels>) {
1024 self.contour_count += 1;
1025 if self.contour_count > 1 {
1026 self.push_triangle(
1027 (self.start, self.current, to),
1028 (point(0., 1.), point(0., 1.), point(0., 1.)),
1029 );
1030 }
1031 self.current = to;
1032 }
1033
1034 pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
1036 self.contour_count += 1;
1037 if self.contour_count > 1 {
1038 self.push_triangle(
1039 (self.start, self.current, to),
1040 (point(0., 1.), point(0., 1.), point(0., 1.)),
1041 );
1042 }
1043
1044 self.push_triangle(
1045 (self.current, ctrl, to),
1046 (point(0., 0.), point(0.5, 0.), point(1., 1.)),
1047 );
1048 self.current = to;
1049 }
1050
1051 pub fn push_triangle(
1053 &mut self,
1054 xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
1055 st: (Point<f32>, Point<f32>, Point<f32>),
1056 ) {
1057 self.bounds = self
1058 .bounds
1059 .union(&Bounds {
1060 origin: xy.0,
1061 size: Default::default(),
1062 })
1063 .union(&Bounds {
1064 origin: xy.1,
1065 size: Default::default(),
1066 })
1067 .union(&Bounds {
1068 origin: xy.2,
1069 size: Default::default(),
1070 });
1071
1072 self.vertices.push(PathVertex {
1073 xy_position: xy.0,
1074 st_position: st.0,
1075 content_mask: Default::default(),
1076 });
1077 self.vertices.push(PathVertex {
1078 xy_position: xy.1,
1079 st_position: st.1,
1080 content_mask: Default::default(),
1081 });
1082 self.vertices.push(PathVertex {
1083 xy_position: xy.2,
1084 st_position: st.2,
1085 content_mask: Default::default(),
1086 });
1087 }
1088}
1089
1090impl<T> Path<T>
1091where
1092 T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
1093{
1094 #[allow(unused)]
1095 #[expect(missing_docs)]
1096 pub fn clipped_bounds(&self) -> Bounds<T> {
1097 self.bounds.intersect(&self.content_mask.bounds)
1098 }
1099}
1100
1101impl From<Path<ScaledPixels>> for Primitive {
1102 fn from(path: Path<ScaledPixels>) -> Self {
1103 Primitive::Path(path)
1104 }
1105}
1106
1107#[derive(Clone, Debug)]
1108#[repr(C)]
1109#[expect(missing_docs)]
1110pub struct PathVertex<P: Clone + Debug + Default + PartialEq> {
1111 pub xy_position: Point<P>,
1112 pub st_position: Point<f32>,
1113 pub content_mask: ContentMask<P>,
1114}
1115
1116#[expect(missing_docs)]
1117impl PathVertex<Pixels> {
1118 pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
1119 PathVertex {
1120 xy_position: self.xy_position.scale(factor),
1121 st_position: self.st_position,
1122 content_mask: self.content_mask.scale(factor),
1123 }
1124 }
1125}